Main Content

このページの翻訳は最新ではありません。ここをクリックして、英語の最新版を参照してください。

CORDIC 回転モード カーネルを使用した正弦と余弦の計算

この例では、MATLAB® の CORDIC 回転モード カーネルを使用して正弦および余弦を計算する方法を説明します。CORDIC ベースのアルゴリズムは、モーター制御、ナビゲーション、信号処理、ワイヤレス通信などのさまざまな組み込みアプリケーションにとって重要です。

はじめに

CORDIC は、COordinate Rotation DIgital Computer の略語です。ギブンス回転に基づく CORDIC アルゴリズム ([1] および [2] を参照) は、反復的なシフト加算演算だけが必要であるため、ハードウェア効率が最も高いアルゴリズムのうちの 1 つです。CORDIC アルゴリズムは、明示的な乗数を必要とせず、正弦関数、余弦関数、逆正弦関数、逆余弦関数、逆正接関数、ベクトル振幅関数、除算関数、平方根関数、双曲線関数、対数関数など、さまざまな関数の計算に適しています。

固定小数点 CORDIC アルゴリズムには次の演算が必要です。

  • 反復あたり 1 回のテーブル ルックアップ

  • 反復あたり 2 回のシフト

  • 反復あたり 3 回の加算

回転計算モードを使用した CORDIC カーネル アルゴリズム

CORDIC 回転計算モード アルゴリズムを使用して、正弦と余弦の同時計算、極座標から直交座標への変換およびその他の演算を計算できます。回転モードでは、振幅ベクトルおよび回転角が既知であるため、回転後に座標 (X-Y) 成分が計算されます。

CORDIC 回転モード アルゴリズムは、必要な回転角で角度のアキュムレータを初期化することによって開始されます。次に、残差角度のアキュムレータの大きさを減らす方法で、CORDIC の反復ごとに回転判定が行われます。回転判定は、各反復後に、角度のアキュムレータ内の残差角度の符号に基づきます。

回転モードでの CORDIC 方程式は以下のとおりです。

$$ z_{i+1} = z_{i} - d_{i}*\mbox{atan}(2^{-i}) $$

$$ x_{i+1} = x_{i} - y_{i}*d_{i}*2^{-i} $$

$$ y_{i+1} = y_{i} + x_{i}*d_{i}*2^{-i} $$

ここで、$$ z_{i} < 0 $$ の場合は $$ d_{i} = -1 $$、それ以外の場合は $$ +1 $$ です。

$$ i = 0, 1, ..., N-1 $$ および $$ N $$ は、反復の総数です。

これにより、$$ N $$$$ +\infty $$ に近づくときに以下の結果が得られます。

$$ z_{N} = 0 $$

$$ x_{N} = A_{N}(x_{0}\cos{z_{0}} - y_{0}\sin{z_{0}}) $$

$$ y_{N} = A_{N}(y_{0}\cos{z_{0}} + x_{0}\sin{z_{0}}) $$

ここで、

$$ A_{N} = \prod_{i=0}^{N-1}{\sqrt{1+2^{-2i}}} $$.

一般的には、$$ N $$ には大きな定数値が選ばれます。そのため、$$ A_{N} $$ は事前に計算できる場合があります。

回転モードでは、CORDIC アルゴリズムは $$ -\pi/2 $$$$ \pi/2 $$ の回転角に制限されます。その範囲外の角度をサポートするには、多くの場合、象限補正が使用されます。

MATLAB への CORDIC 回転モード カーネル アルゴリズムの効率的な実装

CORDIC 回転モード カーネル アルゴリズムを MATLAB コードに実装する例は、スカラー xy および z の場合、次のようになります。固定小数点と浮動小数点の演算の両方にこの同じコードを使用できます。

CORDIC 回転モード カーネル

function [x, y, z] = cordic_rotation_kernel(x, y, z, inpLUT, n)
% Perform CORDIC rotation kernel algorithm for N iterations.
xtmp = x;
ytmp = y;
for idx = 1:n
    if z < 0
        z(:) = accumpos(z, inpLUT(idx));
        x(:) = accumpos(x, ytmp);
        y(:) = accumneg(y, xtmp);
    else
        z(:) = accumneg(z, inpLUT(idx));
        x(:) = accumneg(x, ytmp);
        y(:) = accumpos(y, xtmp);
    end
    xtmp = bitsra(x, idx); % bit-shift-right for multiply by 2^(-idx)
    ytmp = bitsra(y, idx); % bit-shift-right for multiply by 2^(-idx)
end

正規化された入力を使用した CORDIC ベースの正弦および余弦の計算

CORDIC 回転モード カーネルを使用した正弦および余弦の計算

適切な初期値を選択することにより、CORDIC カーネル回転モード アルゴリズムで正弦と余弦の両方を同時に直接計算できます。

最初に、次の初期化手順を実行します。

  • 角度入力ルックアップ テーブル inpLUTatan(2 .^ -(0:N-1)) に設定します。

  • $$ z_{0} $$$$ \theta $$ 入力引数値に設定します。

  • $$ x_{0} $$$$ 1 / A_{N} $$ に設定します。

  • $$ y_{0} $$ をゼロに設定します。

$$ N $$ 回の反復の後、これらの初期値は $$ N $$$$ +\infty $$ に近づくときの以下の出力を導きます。

  • $$ x_{N} \approx cos(\theta) $$

  • $$ y_{N} \approx sin(\theta) $$

その他の回転モード カーネルベースの関数近似は、他の初期条件を使って前処理と後処理を行うと可能です ([1] および [2] を参照)。

CORDIC アルゴリズムは、通常、指定された回数 (定数) の反復を実行します。これは、CORDIC 反復を早期に終了するとパイプライン化されたコードが分断されて $$ n $$ が変化するため、CORDIC ゲイン $$ A_{n} $$ が一定でなくなるためです。

$$ n $$ が非常に大きな値の場合、CORDIC アルゴリズムが収束することは保証されますが、常に単調であるとは限りません。通常、総反復回数を増やすと精度を上げることができます。

整数形式の値を使って回転の測定角度を表す回転角度センサー (サーボ モーター内など) を利用するとします。また、加算、減算、シフトおよびメモリ演算を実行できる 16 ビットの整数演算器も利用するとします。このような演算器の場合、CORDIC 回転モード カーネルを実装すると、乗算や大きいルックアップ テーブルを使用せずに、センサー角度値から正弦と余弦 (直交の X および Y 座標と等価) を効率的に計算できます。

sumWL  = 16; % CORDIC sum word length
thNorm = -1.0:(2^-8):1.0; % Normalized [-1.0, 1.0] angle values
theta  = fi(thNorm, 1, sumWL); % Fixed-point angle values (best precision)

z_NT   = numerictype(theta);             % Data type for Z
xyNT   = numerictype(1, sumWL, sumWL-2); % Data type for X-Y
x_out  = fi(zeros(size(theta)), xyNT);   % X array pre-allocation
y_out  = fi(zeros(size(theta)), xyNT);   % Y array pre-allocation
z_out  = fi(zeros(size(theta)), z_NT);   % Z array pre-allocation

niters = 13; % Number of CORDIC iterations
inpLUT = fi(atan(2 .^ (-((0:(niters-1))'))) .* (2/pi), z_NT); % Normalized
AnGain = prod(sqrt(1+2.^(-2*(0:(niters-1))))); % CORDIC gain
inv_An = 1 / AnGain; % 1/A_n inverse of CORDIC gain

for idx = 1:length(theta)
    % CORDIC rotation kernel iterations
    [x_out(idx), y_out(idx), z_out(idx)] = ...
        fidemo.cordic_rotation_kernel(...
            fi(inv_An, xyNT), fi(0, xyNT), theta(idx), inpLUT, niters);
end

% Plot the CORDIC-approximated sine and cosine values
figure;
subplot(411);
plot(thNorm, x_out);
axis([-1 1 -1 1]);
title('Normalized X Values from CORDIC Rotation Kernel Iterations');
subplot(412);
thetaRadians = pi/2 .* thNorm; % real-world range [-pi/2 pi/2] angle values
plot(thNorm, cos(thetaRadians) - double(x_out));
title('Error between MATLAB COS Reference Values and X Values');
subplot(413);
plot(thNorm, y_out);
axis([-1 1 -1 1]);
title('Normalized Y Values from CORDIC Rotation Kernel Iterations');
subplot(414);
plot(thNorm, sin(thetaRadians) - double(y_out));
title('Error between MATLAB SIN Reference Values and Y Values');

参考文献

  1. Jack E. Volder, The CORDIC Trigonometric Computing Technique, IRE Transactions on Electronic Computers, Volume EC-8, September 1959, pp330-334.

  2. Ray Andraka, A survey of CORDIC algorithm for FPGA based computers, Proceedings of the 1998 ACM/SIGDA sixth international symposium on Field programmable gate arrays, Feb. 22-24, 1998, pp191-200