FPGA · 회전 모드 · AMD PG105 v6.0

FPGA CORDIC: 작은 회전, gain 보정과 고정소수점 오차

시프트와 덧셈으로 각도를 좁혀 갈 수 있지만, 벡터의 길이까지 자동으로 유지되는 것은 아닙니다. 회전 방향, 배율, 수치 표현을 따로 확인합니다.

작은 회전의 수식

CORDIC의 회전 모드(rotation mode)는 목표각을 작은 회전의 합으로 근사합니다. 단계 i에서 사용하는 각도는 atan(2^-i)입니다. 잔여 각 z_i가 양수이면 반시계 방향, 음수이면 시계 방향을 선택하는 부호 관례를 사용하겠습니다. 여기서는 0도 양수 쪽으로 처리합니다.

i = 0, 1, ..., N-1
d_i = +1 if z_i >= 0 else -1
x_(i+1) = x_i - d_i * y_i * 2^-i
y_(i+1) = y_i + d_i * x_i * 2^-i
z_(i+1) = z_i - d_i * atan(2^-i)

x_(i+1)과 y_(i+1)은 반드시 같은 이전 단계의 x_i, y_i로 계산합니다. x를 먼저 덮어쓴 뒤 그 값을 y 계산에 넣으면 다른 알고리즘이 됩니다. 2의 거듭제곱 배율은 이진 고정소수점 구현에서 시프트로 다룰 수 있지만, 부호 확장과 버려지는 하위 비트의 처리는 별도로 정해야 합니다.

회전 모드는 각도를 입력받아 좌표를 회전합니다. y를 0에 가깝게 보내며 각도를 구하는 vectoring 모드와는 방향 결정식과 입력 계약이 다릅니다. 아래 예시를 그대로 atan2 구현으로 사용하지 않습니다.

길이는 왜 늘어나는가

위 좌표식의 제곱합을 전개하면 x_next² + y_next² = (1 + 2^(-2i)) × (x² + y²)입니다. 따라서 한 단계의 길이 배율은 sqrt(1 + 2^(-2i)), N단계의 누적 배율은 다음과 같습니다. 이 글은 1보다 큰 쪽을 K_N으로 정의합니다. 자료에 따라 그 역수를 K라고 부르기도 하므로 이름보다 정의를 비교합니다.

K_N = product(sqrt(1 + 2^(-2i)), i=0..N-1)
(x_0, y_0) = (1/K_N, 0)
(x_N, y_N) ≈ (cos(theta), sin(theta))
x축의 길이 1/K인 초기 벡터가 회전과 길이 증가를 거쳐 30도 방향의 거의 단위 길이 벡터가 되는 끝점 도식
직접 작성한 끝점 도식입니다. 중간 단계의 전체 궤적이나 실제 FPGA 파형은 아닙니다.

(1, 0)으로 시작한 출력에는 K_N이 남습니다. 입력을 미리 줄이거나 출력 배율을 보정하는 방식을 명시해야 합니다. 다만 AMD PG105의 Sin and Cos 기능은 내부 사전 보정을 수행합니다. 이 기능의 정상 출력에 다시 1/K_N을 곱하면 보정을 중복 적용하게 됩니다. 일반 Rotate 기능과 Sin/Cos 기능의 설정을 섞지 않습니다.

16단계 30도 예시

다음은 각도를 라디안으로 계산하는 JavaScript 수치 참고 코드입니다. 정수 비트폭이나 하드웨어 latency를 모델링하지 않습니다. N=16일 때 K_16 ≈ 1.64676026, 1/K_16 ≈ 0.60725294입니다.

function cordic(theta, n = 16) {
  let gain = 1;
  for (let i = 0; i < n; i++) gain *= Math.hypot(1, 2 ** -i);
  let x = 1 / gain, y = 0, z = theta;
  const rows = [];
  for (let i = 0; i < n; i++) {
    const d = z >= 0 ? 1 : -1;
    const oldX = x, oldY = y;
    x = oldX - d * oldY * 2 ** -i;
    y = oldY + d * oldX * 2 ** -i;
    z -= d * Math.atan(2 ** -i);
    rows.push({step: i + 1, x, y, z});
  }
  return {gain, x, y, z, rows};
}
const result = cordic(Math.PI / 6);
30도 목표의 계산값, 소수점 아래 8자리
완료 단계xy잔여 각(도)
10.607252940.60725294-15.00000000
20.910879400.3036264711.56505118
30.834972790.53134632-2.47119229
40.901391080.426974724.65382406
80.863696020.50399290-0.26486139
160.866018120.50001262-0.00083485

정확한 cos(30°)=√3/2, sin(30°)=1/2와 비교하면 유한 단계의 잔여 오차가 보입니다. 좌표 크기가 거의 1이라는 검사만으로 각도가 맞았다고 판단할 수 없습니다. 좌표 오차와 잔여 각을 함께 확인합니다.

사분면과 입력 범위

이 참고 코드에는 전체 원을 위한 사분면 변환이 없습니다. 입력을 그대로 ±180°까지 확대하지 않습니다. 표준 i=0부터의 회전 합이 제공하는 범위와 특정 IP가 허용하는 입력 범위도 같은 개념이 아닙니다. 첫 회전이 45°라고 해서 모든 CORDIC의 한계가 ±45°인 것은 아닙니다.

PG105 v6.0의 Sin/Cos 기능에서는 coarse rotation을 켠 경우 문서의 입력 범위가 -π부터 +π이며, 끄면 -π/4부터 +π/4로 제한됩니다. 이는 해당 IP 구성의 계약입니다. 위 30° 예시는 그 좁은 범위 안에 있지만, 이 수치 코드가 AMD IP와 비트 단위로 같다는 뜻은 아닙니다.

고정소수점과 구조를 함께 정한다

실제 RTL에서는 좌표의 정수부·소수부 폭, 각도의 단위와 표현 폭, atan 테이블의 양자화, 중간 guard bit, 반올림·절삭·overflow 처리가 필요합니다. 단계 수만 늘려도 시프트 결과가 모두 버려지는 비트폭이라면 원하는 정확도를 얻지 못합니다. 포화 연산인지 wraparound인지도 결과가 달라집니다.

구조주요 교환 관계확인할 값
연산기 재사용단계별 연산을 시간으로 나누며 하드웨어를 공유새 입력을 받는 간격, 중간 상태 보존
단계 전개·파이프라인단계마다 자원을 두고 여러 입력을 중첩 처리latency와 initiation interval(II)을 따로 측정

PG105의 word-serial/parallel 선택 역시 면적과 처리량의 교환입니다. 실제 latency는 선택한 기능·파이프라인·인터페이스 설정을 따라야 합니다. backpressure로 흐름이 멈추는 인터페이스에서 “항상 매 주기 결과 하나”를 보장하지 않습니다. 제품의 특정 사이클 수를 이 수치 코드나 다른 RTL의 성능으로 옮겨 적지 않습니다.

Open Logic 4.5.0의 회전 CORDIC 연결

구체적인 RTL 예시로 Open Logic 4.5.0의 olo_fix_cordic_rot을 보면, 크기 In_Mag와 각도 In_Ang를 받아 I/Q 좌표 Out_I·Out_Q를 만듭니다. 각도는 라디안이 아니라 한 바퀴를 1로 둔 부호 없는 고정소수점 값입니다. 예를 들어 0.25는 90도에 대응합니다. 위 JavaScript 예시의 라디안 값을 비트열로 바로 연결하지 않고 단위·형식과 양자화를 변환해야 합니다.

GainCorrCoefFmt_g가 NONE이면 내부 이득 보정을 하지 않습니다. 보정이 켜진 구성에는 외부에서 역이득을 다시 적용하지 않습니다. 같은 버전의 VHDL 형식 상수에서 AUTO 좌표 형식은 부호 비트, 입력 크기보다 하나 많은 정수 비트, 출력보다 세 개 많은 소수 비트를 사용합니다. 이것이 모든 입력·출력 형식에서 overflow가 없다는 보증은 아닙니다. 출력 크기, 반올림·포화 설정과 경계 입력을 함께 대조합니다.

구성새 입력 수락 간격연결할 때 확인할 점
SERIAL정상 연속 동작에서 Iterations_g 주기In_Valid와 In_Ready가 함께 유효한 입력만 수락
PIPELINED매 주기 입력 가능II=1과 첫 결과까지의 latency를 구분

이 블록에는 Out_Ready가 없습니다. 입력을 늦추는 것만으로 이미 파이프라인에 들어온 결과를 즉시 멈출 수 없으므로 후단 수용량과 진행 중인 입력 수를 함께 관리합니다. 유한 FIFO 하나가 길이를 알 수 없는 후단 정지를 무조건 해결하지는 않습니다.

공식 변경 이력에는 4.3.0의 회전·벡터 CORDIC 추가, 4.4.1의 SERIAL 합성 수정, 4.5.0의 AUTO 좌표 형식 수정이 구분되어 있습니다. 이 절은 4.5.0 문서와 RTL의 대조이며, HDL 시뮬레이션·합성·보드 실행 결과가 아닙니다.

English summary. Open Logic 4.5.0 uses unsigned normalized turns, not radians. Check gain compensation and fixed-point formats. SERIAL and PIPELINED have different input intervals; neither provides output backpressure through an Out_Ready port. Version-specific source review is not a synthesis or board result.

검증 범위

위 코드와 표를 자동 검사하고, ±45° 구간의 여러 입력을 Math.sin·Math.cos와 대조했습니다. 이는 수치 참고 모델의 검사이며 HDL 시뮬레이션, FPGA 합성·배치배선, 전력·자원 측정이나 보드 검증은 수행하지 않았습니다. 검증 대상과 도구 단계를 나누는 이유는 netlist와 SDF의 대응에서도 이어집니다.

English summary (not a full translation)

This rotation-mode reference uses simultaneous old x/y values and defines K as the accumulated gain greater than one. A pre-scaled seed compensates the gain; a vendor Sin/Cos block may already do so internally. The 16-step 30-degree example is floating-point arithmetic, not FPGA output. Quadrant handling, fixed-point error, initiation interval and latency require separate contracts. No synthesis or hardware measurements are claimed.

근거