В этом примере показано, как использовать алгоритмы на основе CORDIC и таблицы поиска, предоставляемые Designer™ Fixed-Point, для аппроксимации синуса MATLAB ® (SIN) и косинус (COS) функции. Эффективные алгоритмы синусоиды и косинуса с фиксированной точкой имеют решающее значение для многих встроенных приложений, включая управление двигателями, навигацию, обработку сигналов и беспроводную связь.
Введение
cordiccexp, cordicsincos, cordicsin, и cordiccos функции аппроксимируют MATLAB sin и cos функции, использующие алгоритм на основе CORDIC. CORDIC - аббревиатура от COORDinate Rotation DIgital Computer. Алгоритм CORDIC на основе ротации Givens (см. [1,2]) является одним из наиболее аппаратных эффективных алгоритмов, поскольку требует только итеративных операций добавления сдвига. Алгоритм CORDIC исключает необходимость явных умножителей и подходит для вычисления множества функций, таких как синус, косинус, арксинус, арккосинус, арктангенс, векторная величина, деление, квадратный корень, гиперболические и логарифмические функции.
Для вычисления синуса и косинуса можно использовать режим вычисления вращения CORDIC, а также операции полярно-декартового преобразования. В этом режиме известны величина вектора и угол поворота, и компоненты координат (X-Y) вычисляются после поворота.
Режим вычисления вращения CORDIC
Алгоритм режима поворота CORDIC начинается с инициализации углового накопителя с требуемым углом поворота. Затем решение о повороте при каждой итерации CORDIC выполняется таким образом, что уменьшается величина накопителя остаточного угла. Решение о повороте основано на знаке остаточного угла в угловом накопителе после каждой итерации.
В режиме вращения уравнения CORDIC:



где если
, и 
иначе;
и
является общим числом итераций.
Это дает следующий результат в качестве
подходов:



Где:
.
В режиме вращения алгоритм CORDIC ограничен углами поворота между
и.
Для поддержки углов за пределами этого диапазона, cordiccexp, cordicsincos, cordicsin, и cordiccos функции используют квадрантную коррекцию (включая возможное дополнительное отрицание) после завершения итераций CORDIC.
CORDICSINCOS Код синуса и косинусаВведение
cordicsincos функция вычисляет синус и косинус входных углов в диапазоне [-2 * pi, 2 * pi) с помощью алгоритма CORDIC. Эта функция принимает
угол (радианы) и число итераций в качестве входных аргументов. Функция возвращает аппроксимации синуса и косинуса.
Выходные сигналы вычислений CORDIC масштабируются с помощью коэффициента усиления вращателя. Это усиление обусловлено предварительным масштабированием начального
значения константы.
Инициализация
cordicsincos выполняет следующие шаги инициализации:
Таблица поиска входного угла inpLUT имеет значение atan(2 .^ -(0:N-1)).
имеет
значение входного аргумента.
имеет значение.
имеет нулевое значение.
Разумный выбор начальных значений позволяет алгоритму непосредственно вычислять одновременно как синус, так и косинус. После
итераций эти начальные значения приводят к следующим выводам в качестве
подходов:


Общий код ядра CORDIC с фиксированной и плавающей точками
Код MATLAB для части ядра алгоритма CORDIC (режим ротации) выглядит следующим образом (для случая скаляра x, y, и z). Этот же код используется как для операций с фиксированной запятой, так и для операций с плавающей запятой:
function [x, y, z] = cordic_rotation_kernel(x, y, z, inpLUT, n) % Perform CORDIC rotation kernel algorithm for N kernel iterations. xtmp = x; ytmp = y; for idx = 1:n if z < 0 z(:) = z + inpLUT(idx); x(:) = x + ytmp; y(:) = y - xtmp; else z(:) = z - inpLUT(idx); x(:) = x - ytmp; y(:) = 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
не будет постоянным, потому что
будет изменяться.
Для очень больших значений
алгоритм CORDIC гарантированно сходится, но не всегда монотонно. Как будет показано в следующем примере, промежуточные итерации иногда дают более точные результаты, чем более поздние итерации. Обычно можно достичь большей точности, увеличив общее число итераций.
Пример
В следующем примере итерация 5 обеспечивает лучшую оценку результата, чем итерация 6, и алгоритм CORDIC сходится в более поздних итерациях.
theta = pi/5; % input angle in radians niters = 10; % number of iterations sinTh = sin(theta); % reference result cosTh = cos(theta); % reference result y_sin = zeros(niters, 1); sin_err = zeros(niters, 1); x_cos = zeros(niters, 1); cos_err = zeros(niters, 1); fprintf('\n\nNITERS \tERROR\n'); fprintf('------\t----------\n'); for n = 1:niters [y_sin(n), x_cos(n)] = cordicsincos(theta, n); sin_err(n) = abs(y_sin(n) - sinTh); cos_err(n) = abs(x_cos(n) - cosTh); if n < 10 fprintf(' %d \t %1.8f\n', n, cos_err(n)); else fprintf(' %d \t %1.8f\n', n, cos_err(n)); end end fprintf('\n');
NITERS ERROR ------ ---------- 1 0.10191021 2 0.13966630 3 0.03464449 4 0.03846157 5 0.00020393 6 0.01776952 7 0.00888037 8 0.00436052 9 0.00208192 10 0.00093798
Постройте график ошибки аппроксимации CORDIC на гистограмме
figure(1); clf; bar(1:niters, cos_err(1:niters)); xlabel('Number of iterations','fontsize',12,'fontweight','b'); ylabel('Error','fontsize',12,'fontweight','b'); title('CORDIC approximation error for cos(pi/5) computation',... 'fontsize',12,'fontweight','b'); axis([0 niters 0 0.14]);

Постройте график результатов X-Y для 5 итераций
Niter2Draw = 5; figure(2), clf, hold on plot(cos(0:0.1:pi/2), sin(0:0.1:pi/2), 'b--'); % semi-circle for i=1:Niter2Draw plot([0 x_cos(i)],[0 y_sin(i)], 'LineWidth', 2); % CORDIC iteration result text(x_cos(i),y_sin(i),int2str(i),'fontsize',12,'fontweight','b'); end plot(cos(theta), sin(theta), 'r*', 'MarkerSize', 20); % IDEAL result xlabel('X (COS)','fontsize',12,'fontweight','b') ylabel('Y (SIN)','fontsize',12,'fontweight','b') title('CORDIC iterations for cos(pi/5) computation',... 'fontsize',12,'fontweight','b') axis equal; axis square;

cordicsinСоздание 1024 точек между [-2 * pi, 2 * pi)
stepSize = pi/256; thRadDbl = (-2*pi):stepSize:(2*pi - stepSize); thRadFxp = sfi(thRadDbl, 12); % signed, 12-bit fixed-point values sinThRef = sin(double(thRadFxp)); % reference results
Сравнение результатов КОРДИК с фиксированной точкой и результатов триговой функции с двойной точностью
Используйте 12-разрядные квантованные входные данные и измените число итераций от 4 до 10.
for niters = 4:3:10 cdcSinTh = cordicsin(thRadFxp, niters); errCdcRef = sinThRef - double(cdcSinTh); figure; hold on; axis([-2*pi 2*pi -1.25 1.25]); plot(thRadFxp, sinThRef, 'b'); plot(thRadFxp, cdcSinTh, 'g'); plot(thRadFxp, errCdcRef, 'r'); ylabel('sin(\Theta)','fontsize',12,'fontweight','b'); set(gca,'XTick',-2*pi:pi/2:2*pi); set(gca,'XTickLabel',... {'-2*pi', '-3*pi/2', '-pi', '-pi/2', ... '0', 'pi/2', 'pi', '3*pi/2','2*pi'}); set(gca,'YTick',-1:0.5:1); set(gca,'YTickLabel',{'-1.0','-0.5','0','0.5','1.0'}); ref_str = 'Reference: sin(double(\Theta))'; cdc_str = sprintf('12-bit CORDICSIN; N = %d', niters); err_str = sprintf('Error (max = %f)', max(abs(errCdcRef))); legend(ref_str, cdc_str, err_str); title(cdc_str,'fontsize',12,'fontweight','b'); end



Вычислить ошибку LSB для N = 10
figure; fracLen = cdcSinTh.FractionLength; plot(thRadFxp, abs(errCdcRef) * pow2(fracLen)); set(gca,'XTick',-2*pi:pi/2:2*pi); set(gca,'XTickLabel',... {'-2*pi', '-3*pi/2', '-pi', '-pi/2', ... '0', 'pi/2', 'pi', '3*pi/2','2*pi'}); ylabel(sprintf('LSB Error: 1 LSB = 2^{-%d}',fracLen),'fontsize',12,'fontweight','b'); title('LSB Error: 12-bit CORDICSIN; N=10','fontsize',12,'fontweight','b'); axis([-2*pi 2*pi 0 6]);

Вычислить уровень шума
fft_mag = abs(fft(double(cdcSinTh))); max_mag = max(fft_mag); mag_db = 20*log10(fft_mag/max_mag); figure; hold on; plot(0:1023, mag_db); plot(0:1023, zeros(1,1024),'r--'); % Normalized peak (0 dB) plot(0:1023, -62.*ones(1,1024),'r--'); % Noise floor level ylabel('dB Magnitude','fontsize',12,'fontweight','b'); title('62 dB Noise Floor: 12-bit CORDICSIN; N=10',... 'fontsize',12,'fontweight','b'); % axis([0 1023 -120 0]); full FFT axis([0 round(1024*(pi/8)) -100 10]); % zoom in set(gca,'XTick',[0 round(1024*pi/16) round(1024*pi/8)]); set(gca,'XTickLabel',{'0','pi/16','pi/8'});

CORDICSINCOS Функция с FIACCELФункцию MEX можно создать из кода MATLAB с помощью функции fiaccel MATLAB ®. Как правило, выполнение сгенерированной функции MEX может улучшить скорость моделирования, хотя фактическое улучшение скорости зависит от используемой платформы моделирования. В следующем примере показано, как ускорить работу с фиксированной точкойcordicsincos функция с использованием fiaccel.
fiaccel функция компилирует код MATLAB в функцию MEX. Этот шаг требует создания временного каталога и разрешений на запись в этом каталоге.
tempdirObj = fidemo.fiTempdir('fi_sin_cos_demo');
При объявлении числа итераций константой (например, 10) с использованием coder.newtype('constant',10)скомпилированная таблица ракурса также будет постоянной и, таким образом, не будет вычисляться в каждой итерации. Кроме того, при звонке cordicsincos_mex, вам не нужно будет давать ему входной аргумент для числа итераций. При передаче числа итераций MEX-функция будет иметь ошибку.
Тип данных входных параметров определяет, cordicsincos выполняет вычисления с фиксированной или плавающей запятой. Когда MATLAB генерирует код для этого файла, код создается только для определенного типа данных. Например, если входной аргумент THETA является фиксированной точкой, то генерируется только код с фиксированной точкой.
inp = {thRadFxp, coder.newtype('constant',10)}; % example inputs for the function
fiaccel('cordicsincos', '-o', 'cordicsincos_mex', '-args', inp)
Сначала вычислите синус и косинус, вызвав cordicsincos.
tstart = tic; cordicsincos(thRadFxp,10); telapsed_Mcordicsincos = toc(tstart);
Затем вычислите синус и косинус, вызвав MEX-функцию cordicsincos_mex.
cordicsincos_mex(thRadFxp); % load the MEX file
tstart = tic;
cordicsincos_mex(thRadFxp);
telapsed_MEXcordicsincos = toc(tstart);
Теперь сравните скорость. Введите в командной строке MATLAB следующее, чтобы увидеть улучшение скорости на вашей платформе:
fiaccel_speedup = telapsed_Mcordicsincos/telapsed_MEXcordicsincos;
Для очистки временного каталога выполните следующие команды:
clear cordicsincos_mex;
status = tempdirObj.cleanUp;
SIN и COS Использование таблиц подстановкиСуществует много подходов на основе таблиц поиска, которые могут использоваться для реализации синусоидальных и косинусных приближений с фиксированной точкой. Ниже приведен недорогой подход, основанный на одной таблице поиска с действительным значением и простой линейной интерполяции ближайшего соседа.
sin и cos методы fi объект в конструкторе фиксированных точек аппроксимирует плавающую точку построения MATLAB ®sin и cos с использованием табличного подхода поиска с простой линейной интерполяцией ближайшего соседа между значениями. Этот подход позволяет создать небольшую таблицу поиска с действительным значением и использует простую арифметику.
Использование одной таблицы поиска с действительным значением упрощает вычисление индекса и общую арифметику, необходимую для достижения очень хорошей точности результатов. Эти упрощения обеспечивают относительно высокую производительность, а также относительно низкие требования к памяти.
SIN и COS ВнедрениеРазмер и точность таблицы подстановки
Двумя важными конструктивными соображениями таблицы подстановки являются ее размер и точность. Невозможно создать таблицу для каждого возможного входного значения.
Также невозможно быть полностью точным из-за квантования
или
поиска значений таблицы.
В качестве компромисса конструктор фиксированных точек SIN и COS методы FI использовать 8-битную таблицу поиска как часть их реализации. 8-битная таблица имеет длину всего 256 элементов, поэтому она мала и эффективна. Восемь битов также соответствует размеру байта или слова на многих платформах. Используемая в сочетании с линейной интерполяцией и 16-битовой точностью выходного сигнала (значение таблицы поиска), 8-битовая адресуемая таблица поиска обеспечивает как очень хорошую точность, так и производительность.
Инициализация константы SIN Значения таблицы подстановки
Для простоты реализации, однородности значений таблицы и скорости используется таблица с полным синевавом. Во-первых, четверть волны SIN функция дискретизируется через 64 однородных интервала в диапазоне [0, пи/2) радиан. Выбор подписанного 16-разрядного дробного типа данных с фиксированной точкой для значений таблицы, т.е. tblValsNT = numerictype(1,16,15), обеспечивает наилучшие результаты точности в SIN диапазон выходных данных [-1.0, 1.0). Значения предварительно квантуются до их установки, чтобы избежать предупреждений о переполнении.
tblValsNT = numerictype(1,16,15); quarterSinDblFltPtVals = (sin(2*pi*((0:63) ./ 256)))'; endpointQuantized_Plus1 = 1.0 - double(eps(fi(0,tblValsNT))); halfSinWaveDblFltPtVals = ... [quarterSinDblFltPtVals; ... endpointQuantized_Plus1; ... flipud(quarterSinDblFltPtVals(2:end))]; fullSinWaveDblFltPtVals = ... [halfSinWaveDblFltPtVals; -halfSinWaveDblFltPtVals]; FI_SIN_LUT = fi(fullSinWaveDblFltPtVals, tblValsNT);
Обзор реализации алгоритма
Внедрение Fixed-Point Designer sin и cos методы fi объекты включают в себя сначала отбрасывание вводов углов с фиксированной точкой
(в радианах) на предварительно определенный тип данных в диапазоне [0, 2pi]. Для этого выполняют операцию по модулю 2pi для получения входного значения фиксированной точки inpValInRange в диапазоне [0, 2pi] и приведение к лучшей точности двоичной точки с масштабированием без знака 16-разрядного типа фиксированной точки numerictype(0,16,13):
% Best UNSIGNED type for real-world value range [0, 2*pi], % which maps to fixed-point stored integer vals [0, 51472]. inpInRangeNT = numerictype(0,16,13);
Затем мы получаем 16-битное сохраненное беззнаковое целое значение из этого значения угла FI с фиксированной точкой в диапазоне:
idxUFIX16 = fi(storedInteger(inpValInRange), numerictype(0,16,0));
Мы умножаем сохраненное целое значение на константу нормализации, 65536/51472. Результирующее целое значение будет находиться в полноразмерном диапазоне индексов uint16:
normConst_NT = numerictype(0,32,31); normConstant = fi(65536/51472, normConst_NT); fullScaleIdx = normConstant * idxUFIX16; idxUFIX16(:) = fullScaleIdx;
8 старших битов (MSB) этого полноразмерного 16-битного индекса без знака idxUFIX16 используются для прямого индексирования в 8-битную таблицу поиска синусов. Выполняется два поиска в таблице, один в вычисленном местоположении индекса таблицы. lutValBelow, и один в следующей позиции индекса lutValAbove:
idxUint8MSBs = storedInteger(bitsliceget(idxUFIX16, 16, 9)); zeroBasedIdx = int16(idxUint8MSBs); lutValBelow = FI_SIN_LUT(zeroBasedIdx + 1); lutValAbove = FI_SIN_LUT(zeroBasedIdx + 2);
Остальные 8 младших битов (LSB) idxUFIX16 используются для интерполяции между этими двумя табличными значениями. Значения LSB рассматриваются как нормированный масштабный коэффициент с 8-битовым дробным типом данных rFracNT:
rFracNT = numerictype(0,8,8); % fractional remainder data type
idxFrac8LSBs = reinterpretcast(bitsliceget(idxUFIX16,8,1), rFracNT);
rFraction = idxFrac8LSBs;
Действительное умножение используется для определения взвешенной разницы между двумя точками. Это приводит к простому вычислению (эквивалентному одному произведению и двум суммам) для получения интерполированного результата синуса с фиксированной точкой:
temp = rFraction * (lutValAbove - lutValBelow); rslt = lutValBelow + temp;
Пример
Используя приведенный выше алгоритм, ниже приведен пример таблицы поиска и процесса линейной интерполяции, используемых для вычисления значения SIN для ввода с фиксированной точкой inpValInRange = 0.425 радианы:
% Use an arbitrary input value (e.g., 0.425 radians) inpInRangeNT = numerictype(0,16,13); % best precision, [0, 2*pi] radians inpValInRange = fi(0.425, inpInRangeNT); % arbitrary fixed-point input angle % Normalize its stored integer to get full-scale unsigned 16-bit integer index idxUFIX16 = fi(storedInteger(inpValInRange), numerictype(0,16,0)); normConst_NT = numerictype(0,32,31); normConstant = fi(65536/51472, normConst_NT); fullScaleIdx = normConstant * idxUFIX16; idxUFIX16(:) = fullScaleIdx; % Do two table lookups using unsigned 8-bit integer index (i.e., 8 MSBs) idxUint8MSBs = storedInteger(bitsliceget(idxUFIX16, 16, 9)); zeroBasedIdx = int16(idxUint8MSBs); % zero-based table index value lutValBelow = FI_SIN_LUT(zeroBasedIdx + 1); % 1st table lookup value lutValAbove = FI_SIN_LUT(zeroBasedIdx + 2); % 2nd table lookup value % Do nearest-neighbor interpolation using 8 LSBs (treat as fractional remainder) rFracNT = numerictype(0,8,8); % fractional remainder data type idxFrac8LSBs = reinterpretcast(bitsliceget(idxUFIX16,8,1), rFracNT); rFraction = idxFrac8LSBs; % fractional value for linear interpolation temp = rFraction * (lutValAbove - lutValBelow); rslt = lutValBelow + temp;
Вот график результатов алгоритма:
x_vals = 0:(pi/128):(pi/4); xIdxLo = zeroBasedIdx - 1; xIdxHi = zeroBasedIdx + 4; figure; hold on; axis([x_vals(xIdxLo) x_vals(xIdxHi) 0.25 0.65]); plot(x_vals(xIdxLo:xIdxHi), double(FI_SIN_LUT(xIdxLo:xIdxHi)), 'b^--'); plot([x_vals(zeroBasedIdx+1) x_vals(zeroBasedIdx+2)], ... [lutValBelow lutValAbove], 'k.'); % Closest values plot(0.425, double(rslt), 'r*'); % Interpolated fixed-point result plot(0.425, sin(0.425), 'gs'); % Double precision reference result xlabel('X'); ylabel('SIN(X)'); lut_val_str = 'Fixed-point lookup table values'; near_str = 'Two closest fixed-point LUT values'; interp_str = 'Interpolated fixed-point result'; ref_str = 'Double precision reference value'; legend(lut_val_str, near_str, interp_str, ref_str); title('Fixed-Point Designer Lookup Table Based SIN with Linear Interpolation', ... 'fontsize',12,'fontweight','b');

SINСоздание 1024 точек между [-2 * pi, 2 * pi)
stepSize = pi/256; thRadDbl = (-2*pi):stepSize:(2*pi - stepSize); % double precision floating-point thRadFxp = sfi(thRadDbl, 12); % signed, 12-bit fixed-point inputs
Сравнение результатов SIN с фиксированной точкой и результатов SIN с двойной точностью
fxpSinTh = sin(thRadFxp); % fixed-point results sinThRef = sin(double(thRadFxp)); % reference results errSinRef = sinThRef - double(fxpSinTh); figure; hold on; axis([-2*pi 2*pi -1.25 1.25]); plot(thRadFxp, sinThRef, 'b'); plot(thRadFxp, fxpSinTh, 'g'); plot(thRadFxp, errSinRef, 'r'); ylabel('sin(\Theta)','fontsize',12,'fontweight','b'); set(gca,'XTick',-2*pi:pi/2:2*pi); set(gca,'XTickLabel',... {'-2*pi', '-3*pi/2', '-pi', '-pi/2', ... '0', 'pi/2', 'pi', '3*pi/2','2*pi'}); set(gca,'YTick',-1:0.5:1); set(gca,'YTickLabel',{'-1.0','-0.5','0','0.5','1.0'}); ref_str = 'Reference: sin(double(\Theta))'; fxp_str = sprintf('16-bit Fixed-Point SIN with 12-bit Inputs'); err_str = sprintf('Error (max = %f)', max(abs(errSinRef))); legend(ref_str, fxp_str, err_str); title(fxp_str,'fontsize',12,'fontweight','b');

Вычислить ошибку LSB
figure; fracLen = fxpSinTh.FractionLength; plot(thRadFxp, abs(errSinRef) * pow2(fracLen)); set(gca,'XTick',-2*pi:pi/2:2*pi); set(gca,'XTickLabel',... {'-2*pi', '-3*pi/2', '-pi', '-pi/2', ... '0', 'pi/2', 'pi', '3*pi/2','2*pi'}); ylabel(sprintf('LSB Error: 1 LSB = 2^{-%d}',fracLen),'fontsize',12,'fontweight','b'); title('LSB Error: 16-bit Fixed-Point SIN with 12-bit Inputs','fontsize',12,'fontweight','b'); axis([-2*pi 2*pi 0 8]);

Вычислить уровень шума
fft_mag = abs(fft(double(fxpSinTh))); max_mag = max(fft_mag); mag_db = 20*log10(fft_mag/max_mag); figure; hold on; plot(0:1023, mag_db); plot(0:1023, zeros(1,1024),'r--'); % Normalized peak (0 dB) plot(0:1023, -64.*ones(1,1024),'r--'); % Noise floor level (dB) ylabel('dB Magnitude','fontsize',12,'fontweight','b'); title('64 dB Noise Floor: 16-bit Fixed-Point SIN with 12-bit Inputs',... 'fontsize',12,'fontweight','b'); % axis([0 1023 -120 0]); full FFT axis([0 round(1024*(pi/8)) -100 10]); % zoom in set(gca,'XTick',[0 round(1024*pi/16) round(1024*pi/8)]); set(gca,'XTickLabel',{'0','pi/16','pi/8'});

Алгоритм CORDIC с фиксированной точкой требует выполнения следующих операций:
1 поиск в таблице за одну итерацию
2 сдвига на итерацию
3 добавления на одну итерацию
Упрощенный алгоритм одиночной таблицы подстановки с линейной интерполяцией ближайшего соседа требует выполнения следующих операций:
2 поиска в таблице
1 умножение
2 дополнения
В реальных приложениях выбор алгоритма для вычислений тригонометрических функций с фиксированной точкой обычно зависит от требуемой точности, стоимости и аппаратных ограничений.
close all; % close all figure windows
Jack E. Volder, The CORDIC Trigonometric Computing Technique, IRE Transactions on Electronic Computers, том EC-8, сентябрь 1959, pp330-334.
Рэй Андрака, обзор алгоритма CORDIC для компьютеров на основе FPGA, Труды ACM/SIGDA 1998 года шестой международный симпозиум по полевым программируемым матрицам затворов, 22-24 февраля 1998 года, pp191-200