최근 DSP 제품 동향과 ZSP 코어 ZSP200/ZSP400을 이용한 32비트 FFT - 3부 |
현존하는 많은 코덱을 적용하고, 향후에 나올 수 있는 표준들을 적용하기 위해서는 소프트웨어를 바꿀 수 있는 DSP가 최선의 선택이다. 현재의 제품에서 다음 제품으로 갈 때 시스템을 완전 재설계하지 않고 약간의 변경으로 사용가능 하도록 하려면 호환성이 있는 DSP 시리즈를 가지는 것을 선택하는 것이 중요하다. 글│김재용 차장, 라몬 트롬베타(Ramon Trombetta), 크리시나 V.V.(Krishna V V), ZSP Division of LSI Logic. |
FFT(Fast Fourier Transform)은 통신, 음악, 이미지, 무선, 네비게이션 등 많은 분야에서 사용이 되고 있다. 적용되는 제품에 따라서 FFT 연산은 일반 프로세서, DSP 혹은 하드웨어로 지원이 되기도 한다. DSP로 지원하는 경우 중에서도 가격과 전력소모를 맞추기 위해서는 16비트 고정소수점 연산이 보통 사용된다. 특별한 경우에는 32비트 연산에 적합한 경우가 있다. 이번 호에서는 ZSP200/ZSP400에서 FFT를 효과적으로 구현하는 것에 대해 기술한다.
FFT(Fast Fourier Transform) FFT 연산은 어디에 적용 할 것인지에 따라서 일반적인 프로세서, 고정소수점 혹은 부동소수점 DSP에서 처리할 수 있다. 고정소수점 DSP는 가격과 전력적인 측면에서 다양한 제품에서 살아남을 수 있는 여지를 제공한다. LSI Logic의 ZSP DSP 구조는 16비트 RISC기반의 슈퍼스칼라 구조를 가지고 있다. 현재는 ZSP200, ZSP400으로 구성되는 1세대와 ZSP500, ZSP540, ZSP600을 기반으로 하는 2세대로 구성이 되어 있고, 캐시를 지원하는 ZSP410, ZSP520, ZSP560 버전을 지원하고 있고, 3세대는 개발 진행 중에 있다.
- 입력과 출력 값의 범위, 오버플로우를 방지하기 위해서 스케일링의 사용여부. - 출력의 신호대잡음비, 잡음은 연산 처리 중에 반올림/버림을 통해서 만들어진다. - DSP의 리소스, 실시간 처리를 위해서는 실행 사이클에 제약이 있는데, 알고리즘에 맞게 DSP 리소스를 선택해야 한다. 여기서는 ZSP200과 ZSP400에서 효과적으로 32비트 FFT를 구현하는 방법에 대해서 논한다.
FFT 알고리즘
N 개의 샘플을 가지는 DFT(Discrete Fourier Transform)와 IDFT(Inverse DFT)는 아래와 같은 수식으로 표현된다.
FFT 구현
이제부터는 Radix-2와 Radix-4의 복잡도에 대해서 논하고, 오버플로우, 라운딩과 같은 고정소수점 처리에서의 문제점에 대해서 논한다.
복잡도 FFT를 소프트웨어로 구성할 때 보통은 버터플라이 연산이 가장 안쪽의 루프에서 사용된다. FFT의 주 연산은 버터플라이 연산이라고 봐도 무관하다. 버터플라이 연산을 얼마나 빨리 할 수 있는지가 FFT의 성능을 좌우한다고 볼 수 있다. FFT를 실행하는데 필요한 사이클은 연산기와 밀접한 관계가 있다. 그리고, 병렬처리 가능여부에 따라서 실행 사이클은 크게 변한다. 게다가 중간 값을 가지고 있을 수 있는 레지스터가 얼마나 있는지, 메모리 대역폭이 어느 정도 인지가 영향을 미치게 된다. 또한 입력 혹은 출력을 순차적으로 사용하지 않음으로 인해서 특별히 지원되지 않으면 이를 정렬하는데 약 6%~12%정도의 사이클을 사용해야 한다. 비트리버스(Bit Reversed) 어드레싱을 지원하는 DSP에서는 입력/출력을 정렬하는데 필요한 부가적인 사이클을 없앨 수 있다.
정확도 연산결과의 정확도는 버터플라이 연산을 하면서 발생하는 반올림과 버림에 따라서 달라진다. 반올림(Rounding)은 계산의 중간에서도 필요하지만, 최종 결과를 내기 전에 16비트 혹은 32비트로 결과를 저장 할 때는 필수적이다. 신호대잡음비(SNR, Signal-to-Noise Ratio) 측면에서 반올림이 버림에 비해서 월등히 좋기 때문에 주로 반올림이 사용된다.
오버플로우 방지 16비트 고정소수점 DSP의 정확도 보다는 FFT의 결과값이 더 큰 경우가 많이 발생한다. 버터플라이 연산 내부에서 덧셈/뺄셈을 하면서 결과 값이 커지는 경향이 있는데, Radix-2인 경우에는 그 값이 1+ 2 = 2.414 정도까지 커질 수 있고, Radix-4에서는 4( 2) = 5.657 정도 커질 수 있다. 부동소수점에서는 이러한 것들이 자동으로 고려가 되지만, 고정소수점 DSP에서는 이러한 결과값이 커지는 것을 방지하기 위해서 가드비트(Guard Bits)를 사용한다. 정밀도가 조금 떨어져도 된다면, 입력 값을 미리 줄이는 스케일링(Scaling) 방법도 사용할 수 있다. N=256, Radix-2인 경우에는 입력 값을 1/1154로 줄여야 하는데, 입력 값의 길이가 커질수록 더 많이 조절 해야 하는 단점이 있다. 절충안으로는 입력 값을 약 3비트 정도 여유를 가지도록 조절하고, 연산의 중간 결과에 일정하게 조절해서 작게 만든다면 오버플로우를 방지할 수 있다. 실질적으로 Radix-2에서는 1/2, Radix-4에서는 1/4를 적용하고, 결과값에 2/N 혹은 4/N을 적용할 수 있다. 오버플로우를 방지 하기 위해서는 전체의 입력과 출력에 한번 적용하는 것 보다는 각 버터플라이 단계마다 스케일링을 적용하는 것이 더 좋다. 이렇게 해서도 오버플로우가 나는 경우에는 결과값을 포화(Saturate)시키는 방법이 사용된다.
정밀도 FFT와 IFFT가 필요한 알고리즘에서 16비트 정밀도만으로 각 단계별로 스케일링을 지원한다는 적합하지 않다. 그래서 32비트 정밀도를 적용하는 것이 반드시 필요하다. DSP의 구조에 따라서 32비트의 정밀도를 지원하기 위해서는 16비트 연산에 비해서 약 3.5배에서 8배의 연산량이 필요하다.
ZSP 구조
ZSP 코어의 1세대 2세대는 모두 로드/스토어 기반의 슈퍼스칼라 RISC 구조이다. 각 명령어는 코어가 실시간으로 동시 실행가능성을 판단해서 실행된다. 실시간으로 동시에 실행 가능한 명령어를 판단하는 것은 VLIW DSP와 구분되는 큰 장점 중에 하나이다. ZSP 1세대 DSP에서는 단일메모리 혹은 프로그램/데이터 메모리 각각을 지원한다. ZSP200과 ZSP400에서는 FFT연산에 필요한 하드웨어 루핑(Looping), 특별한 어드레싱모드 지원, 비트 연산 등이 지원된다. RISC같은 구조를 가지고 있어서 명령어가 단순하고 컴파일러에서 좋은 결과를 낼 수가 있다. 모든 ZSP 코어는 16비트의 기본 명령어를 지원하고, 상위 코어계열로 호환성으로 가지고 있다.
FFT에 관련된 구조 모든 ZSP 코어에서 CMULR과 CMULI 명령어를 사용할 수 있는데, 이들 명령어는 두 번의 곱셈과 한번의 덧셈 혹은 뺄셈을 수행한다. 이 두 명령어를 합하면 완전한 복소수 곱셈이 된다. ZSP200에서는 각각의 명령어를 실행하는데 2 사이클이, ZSP400에서는 1 사이클이 필요하다. 즉, ZSP200에서는 완전한 복소수 곱셈을 하는데 4 사이클이, ZSP400에서는 2 사이클이 필요하다. 이러한 복소수 곱셈에서도 추가적인 사이클 없이 반올림이 선택적으로 지원된다. 32비트 FFT 연산을 위해서는 DMUL와 DMAC 명령어가 사용되고, 스케일링은 SHRA.E 명령어를 사용한다. FFT와 관련된 기능 중에 하나는 비트리버스 어드레싱인데, 이것 또한 설정하는데 필요한 약 5 사이클을 제외하면 추가적인 사이클은 필요치 않다. REVB 명령어를 사용해서 각 비트의 내용을 반전시키는 기능을 사용할 수도 있다. 모든 ZSP 코어에서는 2개의 16비트 데이터를 읽거나 쓰는 명령어가 지원된다. 모든 어드레싱 모드는 두 개의 16비트 데이터를 처리할 때도 그대로 사용할 수 있다. 어드레싱 모드로서는 최대 4개까지의 독립적인 Circular 버퍼를 지정할 수 있다. 시작번지와 끝 번지를 지정 함으로서 ZSP 코어가 자동적으로 번지를 계산한다. 이 기능은 FFT 구현 중에는 트위들팩터를 읽어 들일 때 사용된다.
1세대 ZSP200
ZSP200의 특징은 아래와 같다. - 두 개의 데이터 포트 - 16개의 16비트 레지스터, 쌍을 이루어서 8개의 32비트 레지스터로 사용될 수 있고, 이 중에는 2개의 40비트 Accumulator가 포함되어 있다.
- 32비트용 Dual MAC 명령어는 2 사이클이 필요하다. 기능적으로는 ZSP400과 완전히 호환이 된다. 1세대 ZSP400 코어 2개의 MAC을 가지고 4개의 명령어를 동시에 수행할 수 있다. 64비트의 로드 포트를 가지고 있어서 최대 4개의 16비트 데이터를 메모리에서 읽어와서 동시에 연산을 처리할 수 있다. 연산결과를 메모리에 저장해야 할 때는 32비트의 스토어 포트를 사용한다. ZSP400의 모든 명령어는 한 사이클에 실행이 가능하다.
FFT 구현
레지스터 파일이 입력/출력 뿐만 아니라 어드레스 포인터에도 사용된다는 것을 고려하면 ZSP200과 ZSP400은 Radix-4보다는 Radix-2가 적합하다. 표 3에서는 ZSP400에서 16비트 FFT를 구현하기 위한 Radix-2 DIF 버터플라이 연산을 보여준다. 스케일링과 라운딩까지 포함해서 4사이클을 필요하고, ZSP200에서는 10사이클이 필요하다. 2세대 ZSP 코어에서는 독립된 어드레스 레지스터가 있어서 Radix-4 FFT연산에도 적용할 수 있고, FFT 처리 비용측면에서 사이클을 더 줄일 수가 있다.
16비트 고정 소수점 FFT 복소수 트위들팩터는 마지막 2개의 값을 제외하고는 미리 계산되어 메모리에 저장되기 때문에 2¶(N-4) 워드의 ROM이 필요하다. 메모리 사용량에서는 ZSP200과 ZSP400에서 동일하다. 하지만 연산처리 측면에서는 ZSP200은 버터플라이 연산당 10.5사이클을 사용하고, ZSP400에서는 약 4.44 사이클을 사용한다. 32비트 고정소수점 FFT 메모리의 크기는 16비트에 비례해서 사용되고, 실행 사이클 측면에서는 ZSP200은 버터플라이당 24.9 사이클이 필요하고, ZSP400에서는 약 14.5 사이클이 필요하다. 약 3.5배의 연산량 증가만으로도 16비트 버전에 비해서는 엄청난 SNR의 개선효과를 얻을 수 있다.
결 론
ZSP200과 ZSP400은 적은 코드 사이즈뿐만 아니라 32비트 FFT 처리를 위한 계산 량 측면에서 좋은 성능을 보여준다. 더 높은 성능을 요구하는 알고리즘에서는 2세대 ZSP 코어가 사용될 수 있다. 16비트 FFT의 성능에 빠짐이 없는 ZSP200 코어에서도 전체적으로 약 2.4배의 자원을 추가해면 32비트의 성능을 낼 수 있다. 이번 호에서는 256 복소수 FFT에 대해서만 고려하였지만, 실수 FFT연산이나 더 큰 크기의 복소수 입력에 대해서도 비슷한 방법으로 구현할 수 있다.
<기사제공: 월간 반도체네트워크 2006년 04월호> |