Methoden zur Berechnung von Fixpunkt atan2 auf FPGA


12

Ich muss atan2(x,y)auf einem FPGA mit einem kontinuierlichen Eingabe- / Ausgabestrom von Daten rechnen. Ich habe es geschafft, es mit nicht gerollten CORDIC-Kerneln in Pipelines zu implementieren, aber um die Genauigkeit zu erreichen, die ich benötige, musste ich 32 Iterationen durchführen. Dies führte dazu, dass eine große Anzahl von LUTs dieser einen Aufgabe gewidmet war. Ich habe versucht, den Fluss so zu ändern, dass teilweise entrollte CORDIC-Kernel verwendet werden, aber dann brauchte ich eine multiplizierte Taktfrequenz, um wiederholte Schleifen auszuführen und trotzdem einen kontinuierlichen Eingabe- / Ausgabe-Fluss aufrechtzuerhalten. Damit konnte ich das Timing nicht einhalten.

Jetzt suche ich nach alternativen Möglichkeiten für die Datenverarbeitung atan2(x,y).

Ich dachte darüber nach, Block-RAM-Nachschlagetabellen mit Interpolation zu verwenden, aber da es 2 Variablen gibt, würde ich 2 Dimensionen von Nachschlagetabellen benötigen, und dies ist im Hinblick auf die Block-RAM-Verwendung sehr ressourcenintensiv.

Ich habe dann darüber nachgedacht, die Tatsache zu nutzen, atan2(x,y)die atan(x/y)mit der Quadrantenanpassung zusammenhängt. Das Problem dabei ist, dass x/yeine echte Teilung erforderlich ist, da dies ykeine Konstante ist und die Teilung auf FPGAs sehr ressourcenintensiv ist.

Gibt es neuere Möglichkeiten zur Implementierung atan2(x,y)auf einem FPGA, die zu einer geringeren LUT-Nutzung führen, aber dennoch eine gute Genauigkeit bieten?


2
Wie hoch ist Ihre Verarbeitungstaktrate und Ihre Eingangsdatenrate?
Jim Clay

Welche Genauigkeit benötigen Sie? Ich gehe auch davon aus, dass Sie die Festkomma-Berechnung verwenden. Welche Bittiefe verwenden Sie? Eine Polynomapproximation (oder LUT) mit Quadrantenanpassung ist eine häufig zu implementierende Methode atan2. Bin mir aber nicht sicher, ob du ohne Division auskommen kannst.
Jason R

Der Eingangstakt beträgt 150 MHz, die Eingangsdatenrate beträgt 150 MSamps / s. Grundsätzlich bekomme ich jeden Takt einen neuen Eingang. Die Latenz ist in Ordnung, aber ich muss auch eine Ausgabe mit 150 MSamps / Sek. Produzieren.
user2913869

Meine Simulationen zeigen, dass ich mit ungefähr 1 * 10 ^ -9 leben kann. Ich bin mir nicht sicher, ob die absoluten minimalen Fixpunktbits vorhanden sind, aber ich habe mit einem Q10.32-Fixpunktformat simuliert
user2913869

Dieser Artikel beschreibt eine Festkommaimplementierung von atan2. Sie benötigen jedoch noch eine Unterteilung.
Matt L.

Antworten:


20

Sie können Logarithmen verwenden, um die Division zu entfernen. Für (x,y) im ersten Quadranten:

z=Log2(y)-Log2(x)atan2(y,x)=eine Lohe(y/x)=eine Lohe(2z)

atan (2 ^ z)

Abbildung 1. Diagramm von eine Lohe(2z)

Sie müssten sich einem ( 2 z ) im Bereich von - 30 < z < 30 eine Lohe(2z) , um die erforderliche Genauigkeit von 1E-9 zu erhalten. Sie können die Symmetrie atan ( 2 - z ) = π ausnutzen-30<z<30eine Lohe(2-z)=π2-eine Lohe(2z)oder stellen Sie alternativ sicher, dass(x,y)in einem bekannten Oktanten ist. Log2(ein)approximieren:

b=Fußboden(Log2(ein))c=ein2bLog2(ein)=b+Log2(c)

b kann berechnet werden, indem der Ort des höchstwertigen Nicht-Null-Bits ermittelt wird. c kann durch eine Bitverschiebung berechnet werden. Sie müsstenLog2(c) im Bereich1c<2 approximieren.

log2 (c)

Abbildung 2. Diagramm von Log2(c)

Für Ihre Genauigkeitsanforderungen, lineare Interpolation und gleichmäßige Abtastung, 214+1=16385 Abtastungen von log2(c) und 30×212+1=122881 Abtastungen vonatan(2z) für0<z<30 ausreichen. Der letztere Tisch ist ziemlich groß. Der Fehler durch Interpolation hängt dabei stark vonz :

Fehler der atan (2 ^ z) -Näherung

Abbildung 3. Größter absoluter Fehler bei einer ( 2 z ) atan(2z) für verschiedene Bereiche von z (horizontale Achse) für verschiedene Anzahlen von Stichproben (32 bis 8192) pro Einheitsintervall von z . Der größte absolute Fehler für 0z<1 (weggelassen) ist geringfügig kleiner als für floor(log2(z))=0 .

Die eine Lohe(2z) -Tabelle kann in mehrere Untertabellen aufgeteilt werden, die 0z<1 und in verschiedene Fußboden(Log2(z)) mit z1 , was einfach zu berechnen ist. Die Tabellenlängen können gemäß Abb. 3 gewählt werden. Der Index innerhalb der Untertabelle kann durch einfache Manipulation von Bitfolgen berechnet werden. Für Ihre Genauigkeitsanforderungen haben die eine Lohe(2z) -Untertabellen insgesamt 29217 Samples, wenn Sie den Bereich von z bis 0z<32 zur Vereinfachung.

Zum späteren Nachschlagen hier das klobige Python-Skript, mit dem ich die Approximationsfehler berechnet habe:

from numpy import *
from math import *
N = 10
M = 20
x = array(range(N + 1))/double(N) + 1
y = empty(N + 1, double)
for i in range(N + 1):
    y[i] = log(x[i], 2)

maxErr = 0
for i in range(N):
    for j in range(M):
        a = y[i] + (y[i + 1] - y[i])*j/M
        if N*M < 1000: 
            print str((i*M + j)/double(N*M) + 1) + ' ' + str(a)
        b = log((i*M + j)/double(N*M) + 1, 2)
        err = abs(a - b)
        if err > maxErr:
            maxErr = err

print maxErr

y2 = empty(N + 1, double)
for i in range(1, N):
    y2[i] = -1.0/16.0*y[i-1] + 9.0/8.0*y[i] - 1.0/16.0*y[i+1]


y2[0] = -1.0/16.0*log(-1.0/N + 1, 2) + 9.0/8.0*y[0] - 1.0/16.0*y[1]
y2[N] = -1.0/16.0*y[N-1] + 9.0/8.0*y[N] - 1.0/16.0*log((N+1.0)/N + 1, 2)

maxErr = 0
for i in range(N):
    for j in range(M):
        a = y2[i] + (y2[i + 1] - y2[i])*j/M
        b = log((i*M + j)/double(N*M) + 1, 2)
        if N*M < 1000: 
            print a
        err = abs(a - b)
        if err > maxErr:
            maxErr = err

print maxErr

y2[0] = 15.0/16.0*y[0] + 1.0/8.0*y[1] - 1.0/16.0*y[2]
y2[N] = -1.0/16.0*y[N - 2] + 1.0/8.0*y[N - 1] + 15.0/16.0*y[N]

maxErr = 0
for i in range(N):
    for j in range(M):
        a = y2[i] + (y2[i + 1] - y2[i])*j/M
        b = log((i*M + j)/double(N*M) + 1, 2)
        if N*M < 1000: 
            print str(a) + ' ' + str(b)
        err = abs(a - b)
        if err > maxErr:
            maxErr = err

print maxErr

P = 32
NN = 13
M = 8
for k in range(NN):
    N = 2**k
    x = array(range(N*P + 1))/double(N)
    y = empty((N*P + 1, NN), double)
    maxErr = zeros(P)
    for i in range(N*P + 1):
        y[i] = atan(2**x[i])

    for i in range(N*P):
        for j in range(M):
            a = y[i] + (y[i + 1] - y[i])*j/M
            b = atan(2**((i*M + j)/double(N*M)))
            err = abs(a - b)
            if (i*M + j > 0 and err > maxErr[int(i/N)]):
                maxErr[int(i/N)] = err

    print N
    for i in range(P):
        print str(i) + " " + str(maxErr[i])    

Der lokale Maximalfehler von Approximieren einer Funktion f(x) durch lineare Interpolation ff^(x) aus Proben vonf(x) , mit Abtastintervall durch gleichförmige Abtastung gemachtΔx kann durch analytisches angenähert werden:

f^(x)-f(x)(Δx)2limΔx0f(x)+f(x+Δx)2-f(x+Δx2)(Δx)2=(Δx)2f(x)8,

wo f(x) die zweite Ableitung vonf(x) undx ist ein lokales Maximum des absoluten Fehlers. Mit dem obigen erhalten wir die Annäherungen:

atan^(2z)atan(2z)(Δz)22z(14z)ln(2)28(4z+1)2,log2^(a)log2(a)(Δa)28a2ln(2).

Da die Funktionen konkav sind und die Abtastwerte mit der Funktion übereinstimmen, liegt der Fehler immer in einer Richtung. Der lokale maximale absolute Fehler könnte halbiert werden, wenn das Vorzeichen des Fehlers einmal in jedem Abtastintervall hin und her wechselt. Mit linearer Interpolation können nahezu optimale Ergebnisse erzielt werden, indem jede Tabelle vorgefiltert wird durch:

y[k]={b0x[k]+b1x[k+1]+b2x[k+2]if k=0,c1x[k1]+c0x[k]+c1x[k+1]if 0<k<N,b2x[k2]+b1x[k1]+b0x[k]if k=N,

Dabei sind x und y die ursprüngliche und die gefilterte Tabelle, die beide 0kNc0=98,c1=116,b0=1516,b1=18,b2=116c0,c1N

(Δx)NlimΔx0(c1f(xΔx)+c0f(x)+c1f(x+Δx))(1a)+(c1f(x)+c0f(x+Δx)+c1f(x+2Δx))af(x+aΔx)(Δx)N={(c0+2c11)f(x)if N=0,|c1=1c020if N=1,1+aa2c02(Δx)2f(x)if N=2,|c0=98

0a<1f(x)f(x)=exb0,b1,b2

(Δx)NlimΔx0(b0f(x)+b1f(x+Δx)+b2f(x+2Δx))(1a)+(c1f(x)+c0f(x+Δx)+c1f(x+2Δx))af(x+aΔx)(Δx)N={(b0+b1+b21+a(1b0b1b2))f(x)if N=0,|b2=1b0b1(a1)(2b0+b12)Δxf(x)if N=1,|b1=22b0(12a2+(2316b0)a+b01)(Δx)2f(x)if N=2,|b0=1516

for 0a<1. Use of the prefilter about halves the approximation error and is easier to do than full optimization of the tables.

Approximation error with and without prefilter and end conditioning

Figure 4. Approximation error of log2(a) from 11 samples, with and without prefilter and with and without end conditioning. Without end conditioning the prefilter has access to values of the function just outside of the table.

This article probably presents a very similar algorithm: R. Gutierrez, V. Torres, and J. Valls, “FPGA-implementation of atan(Y/X) based on logarithmic transformation and LUT-based techniques,Journal of Systems Architecture, vol. 56, 2010. The abstract says their implementation beats previous CORDIC-based algorithms in speed and LUT-based algorithms in footprint size.


3
Matthew Gambrell and I have reverse engineered the 1985 Yamaha YM3812 sound chip (by microscopy) and found in it similar log/exp read only memory (ROM) tables. Yamaha had used an additional trick of replacing every second entry in each table by a difference to the previous entry. For smooth functions, difference takes less bits and chip area to represent than the function. They already had an adder on the chip that they were able to use to add the difference to the previous entry.
Olli Niemitalo

3
Vielen Dank! Ich liebe diese Art von Exploits mathematischer Eigenschaften. Ich werde auf jeden Fall einige MATLAB-Sims davon entwickeln und wenn alles gut aussieht, auf HDL umsteigen. Ich werde meine LUT-Einsparungen zurückmelden, wenn alles erledigt ist.
user2913869

I used your description as a guide and I am happy to stay that I reduced by LUTs by almost 60%. I did have a need to reduce the BRAMs, so I figured out I could get a consistent max error on my ATAN table by doing non-uniform sampling: I had multiple LUT BRAMs (all the same number of address bits), the closer to zero, the faster the sampling. I chose my table ranges to be powers of 2 so I could easily detect which range I was in as well and do automatic table indexing via bit manipulation. I applied atan symmetry as well so I only stored half the waveform.
user2913869

Möglicherweise habe ich auch einige Ihrer Änderungen verpasst, aber ich konnte 2 ^ z implementieren, indem ich es in 2 ^ {if} = 2 ^ i * 2 ^ {0.f} aufteilte, wobei i der ganzzahlige Teil und f der ganzzahlige Teil ist der Bruchteil. 2 ^ i ist einfach, nur Bit-Manipulation, und 2 ^ {0.f} hatte einen begrenzten Bereich, so dass es sich gut für die LUT mit Interpolation eignet. Ich habe auch den negativen Fall behandelt: 2 ^ {- if} = 2 ^ {- i} * 1 / (2 ^ {0.f}. Also noch eine Tabelle für 1/2 ^ {0.f}. Mein nächster Schritt Möglicherweise wird die Potenz von 2 Messbereichen / ungleichmäßiger Abtastung auf die log2 (y) -LUTs angewendet, da dies anscheinend der perfekte Kandidat für eine solche Wellenform ist.
user2913869

1
Lol yup I totally missed that step. I'm going to try that now. Should save me even more LUTs and even more BRAMs
user2913869
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.