FPGA에서 고정 소수점 atan2를 계산하는 방법


12

atan2(x,y)지속적인 입 / 출력 데이터 스트림을 사용하여 FPGA 에서 컴퓨팅해야 합니다. 롤링되지 않은 파이프 라인 된 CORDIC 커널을 사용하여 구현했지만 필요한 정확도를 얻으려면 32 번의 반복을 수행해야했습니다. 이로 인해 많은 양의 LUT가이 하나의 작업에 전념하게되었습니다. 부분적으로 롤링되지 않은 CORDIC 커널을 사용하도록 흐름을 변경하려고 시도했지만 계속적인 입력 / 출력 흐름을 유지하면서 반복 루프를 실행하기 위해 곱해진 클럭 주파수가 필요했습니다. 이것으로 타이밍을 맞출 수 없었습니다.

그래서 지금은 다른 컴퓨팅 방법을 찾고 atan2(x,y)있습니다.

보간과 함께 블록 RAM 조회 테이블을 사용하는 것에 대해 생각했지만 2 개의 변수가 있으므로 2 차원의 조회 테이블이 필요하며 이는 블록 RAM 사용 측면에서 리소스를 많이 사용합니다.

그런 다음 사분면 조정과 atan2(x,y)관련된 사실을 사용하는 것에 대해 생각했습니다 atan(x/y). 이것에 대한 문제 는 상수가 아니기 x/y때문에 진정한 분할 이 필요하며 yFPGA의 분할은 매우 자원 집약적이라는 것입니다.

더 atan2(x,y)낮은 LUT 사용을 초래하지만 여전히 우수한 정확도를 제공하는 FPGA 에 구현할 수있는 더 새로운 방법이 있습니까?


2
처리 클럭 속도와 입력 데이터 속도는 무엇입니까?
— Jim Clay

필요한 정확도는 무엇입니까? 나는 또한 고정 소수점 계산을 사용한다고 가정합니다. 어떤 비트 심도를 사용하고 있습니까? 사분면 조정을 사용한 다항식 근사 (또는 LUT)는 구현하는 일반적인 방법 atan2입니다. 그러나 부서없이 갈 수 있는지 확실하지 않습니다.
— Jason R

입력 클럭은 150MHz이고 입력 데이터 속도는 150MSamps / sec입니다. 기본적으로 클럭 사이클마다 새로운 입력을 얻습니다. 대기 시간은 좋지만 초당 150MSamps로 출력해야합니다.
— user2913869

내 시뮬레이션은 약 1 * 10 ^ -9로 살 수 있음을 보여줍니다. 절대 최소 고정 소수점 비트는 확실하지 않지만 Q10.32 고정 소수점 형식으로 시뮬레이션했습니다.
— user2913869

이 기사에서는 의 고정 소수점 구현에 대해 설명합니다 atan2. 그래도 여전히 부서가 필요합니다.
— Matt L.

답변:


20

로그를 사용하여 나누기를 제거 할 수 있습니다. 옵션 (x,y) 제 사분면 :

z=log2⁡(y)−log2⁡(x)atan2(y,x)=atan(y/x)=atan(2z)

아탄 (2 ^ z)

도 1의 플롯 atan(2z)

atan(2z)−30<z<30atan(2−z)=π2−atan(2z)(x,y)log2⁡(a)

b=floor(log2⁡(a))c=a2blog2⁡(a)=b+log2⁡(c)

bclog2⁡(c)1≤c<2

log2 (c)

log2⁡(c)

214+1=16385log2⁡(c)30×212+1=122881atan(2z)0<z<30z

아탄 (2 ^ z) 근사치의 오차

atan(2z)zz0≤z<1floor(log2⁡(z))=0

atan(2z)0≤z<1floor(log2⁡(z))z≥1atan(2z)z0≤z<32

나중에 참조 할 수 있도록 근사 오류를 계산하는 데 사용 된 이상한 파이썬 스크립트는 다음과 같습니다.

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])    

f(x)f^(x)f(x)Δx

f^(x)−f(x)≈(Δx)2limΔx→0f(x)+f(x+Δx)2−f(x+Δx2)(Δx)2=(Δx)2f″(x)8,

여기서 는 의 2 차 미분 이고 는 절대 오차의 극대값입니다. 위와 같이 근사값을 얻습니다.f″(x)f(x)x

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

함수가 오목하고 샘플이 함수와 일치하기 때문에 오류는 항상 한 방향입니다. 샘플링 간격마다 한 번씩 에러의 부호가 번갈아 바뀌면 로컬 최대 절대 오차가 절반으로 줄어들 수 있습니다. 선형 보간을 사용하면 다음을 통해 각 테이블을 사전 필터링하여 최적의 결과에 근접 할 수 있습니다.

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

여기서 와 는 원본이고 필터링 된 테이블 모두 걸쳐 있으며 가중치는 . 최종 조건화 (위 방정식의 첫 번째 행과 마지막 행)는 테이블 외부 함수의 샘플을 사용하는 것과 비교하여 테이블 끝의 오류를 줄입니다. 첫 번째와 마지막 샘플은 보간으로 인한 오류를 줄이기 위해 조정할 필요가 없기 때문입니다. 그것과 테이블 바깥의 샘플 사이. 샘플링 간격이 다른 서브 테이블은 별도로 사전 필터링해야합니다. 지수 의 값은 지수 을 증가시키기 위해 순차적으로 최소화함으로써 발견되었습니다.xy0≤k≤Nc0=98,c1=−116,b0=1516,b1=18,b2=−116c0,c1N 근사 오차의 최대 절대 값 :

(Δx)NlimΔx→0(c1f(x−Δx)+c0f(x)+c1f(x+Δx))(1−a)+(c1f(x)+c0f(x+Δx)+c1f(x+2Δx))a−f(x+aΔx)(Δx)N={(c0+2c1−1)f(x)if N=0,|c1=1−c020if N=1,1+a−a2−c02(Δx)2f″(x)if N=2,|c0=98

샘플 간 보간 위치 에 대해 오목 또는 볼록 함수 (예 : ). 이러한 가중치가 해결되면 다음 과 같은 최대 절대 값을 마찬가지로 최소화 하여 최종 조정 가중치 을 찾았습니다.0≤a<1f(x)f(x)=exb0,b1,b2

(Δx)NlimΔx→0(b0f(x)+b1f(x+Δx)+b2f(x+2Δx))(1−a)+(c1f(x)+c0f(x+Δx)+c1f(x+2Δx))a−f(x+aΔx)(Δx)N={(b0+b1+b2−1+a(1−b0−b1−b2))f(x)if N=0,|b2=1−b0−b1(a−1)(2b0+b1−2)Δxf′(x)if N=1,|b1=2−2b0(−12a2+(2316−b0)a+b0−1)(Δx)2f″(x)if N=2,|b0=1516

대 . 근사 오차를 절반으로 줄이는 프리 필터를 사용하면 테이블을 완전히 최적화하는 것보다 수행하기가 더 쉽습니다.0≤a<1

프리 필터 및 엔드 컨디셔닝 유무에 따른 근사 오차

그림 4. 프리 필터 유무에 따라 그리고 최종 컨디셔닝 유무에 관계없이 11 개 샘플에서 의 근사 오차 . 최종 컨디셔닝없이 프리 필터는 테이블 바로 바깥에서 기능 값에 액세스 할 수 있습니다.log2⁡(a)

이 문서는 아마 매우 유사한 알고리즘을 제공한다 : R. 레즈, V. 토레스 및 J. 발스 '를 ATAN의 FPGA - 구현 (Y / X), 대수 변환 및 LUT 기반의 기술에 기초하여, " 시스템 아키텍처 저널 , 권 . 초록은 그들의 구현이 이전의 CORDIC 기반 알고리즘 속도보다 빠르며 LUT 기반 알고리즘은 풋 프린트 크기보다 뛰어납니다.


3
Matthew Gambrell과 저는 1985 년 Yamaha YM3812 사운드 칩 (현미경 검사)을 리버스 엔지니어링했으며 비슷한 log / exp 읽기 전용 메모리 (ROM) 테이블을 찾았습니다. Yamaha는 각 테이블의 두 번째 항목을 이전 항목과의 차이로 대체하는 추가 트릭을 사용했습니다. 부드러운 기능을 위해 차이는 기능보다 적은 비트와 칩 면적을 나타냅니다. 그들은 이미 칩에 가산기를 가지고 있었기 때문에 이전 항목에 차이를 더할 수있었습니다.
— Olli Niemitalo

3
대단히 감사합니다! 나는 이런 종류의 수학적 속성을 좋아합니다. 나는 이것에 대한 MATLAB 시뮬레이션을 확실히 개발할 것이며 모든 것이 잘 보인다면 HDL로 넘어가십시오. 모든 것이 끝나면 LUT 절감액을 다시보고 할 것입니다.
— user2913869

나는 당신의 설명을 가이드로 사용했으며 LUT에 의해 거의 60 % 줄어든 것을 기쁘게 생각합니다. 나는 BRAM을 줄일 필요가 있었으므로 불균일 샘플링을 수행하여 ATAN 테이블에서 일관된 최대 오류를 얻을 수 있다고 생각했습니다. 0은 샘플링 속도가 빠릅니다. 나는 테이블 범위를 2의 거듭 제곱으로 선택했기 때문에 내가 속한 범위를 쉽게 감지하고 비트 조작을 통해 자동 테이블 인덱싱을 수행 할 수 있습니다. 나는 atan 대칭도 적용하여 파형의 절반 만 저장했습니다.
— user2913869

또한 편집 내용 중 일부를 놓쳤을 수도 있지만 2 ^ z를 2 ^ {if} = 2 ^ i * 2 ^ {0.f}로 나누면 2 ^ z를 구현할 수 있습니다. 여기서 i는 정수 부분이고 f는 소수 부분. 2 ^ i는 단순하고 비트 조작이 간단하며 2 ^ {0.f}의 범위는 제한되어 있으므로 보간을 통해 LUT에 적합합니다. 또한 2 ^ {-if} = 2 ^ {-i} * 1 / (2 ^ {0.f}이므로 1 / 2 ^ {0.f}에 대한 테이블이 하나 더 있습니다. 다음 단계 log2 (y) LUT에 2 개의 레인 징 / 비 균일 샘플링의 파워를 적용하는 것이 될 수 있습니다. 그것이 그런 종류의 완벽한 후보 파형 인 것처럼 보입니다.
— user2913869

1
롤 yu 나는 그 단계를 완전히 놓쳤다. 나는 지금 그것을 시도 할 것입니다. 더 많은 LUT와 더 많은 BRAM을 절약해야합니다.
— user2913869
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.