Лучшие практики ручного преобразования с фиксированной точкой

Программное обеспечение Fixed-Point Designer™ помогает вам проектировать и преобразовывать алгоритмы в фиксированную точку. Разрабатываете ли вы просто алгоритмы с фиксированной точкой в MATLAB® или использование Fixed-Point Designer в сочетании с MathWorks® продукты генерации кода, эти лучшие практики помогают вам получить от типового кода MATLAB к эффективной реализации с фиксированной точкой. Эти лучшие практики также описаны в этом вебинаре: Руководство по преобразованию фиксированных точек Лучшие практики Вебинар

Создайте тестовый файл

Лучшая практика для структурирования вашего кода состоит в том, чтобы отделить ваш основной алгоритм от другого кода, который вы используете для тестирования и проверки результатов. Создайте тестовый файл, чтобы вызвать ваш исходный алгоритм MATLAB и версии алгоритма с фиксированной точкой. Например, как показано в следующей таблице, вы можете настроить некоторые входные данные для подачи в алгоритм, а затем, после обработки этих данных, создать некоторые графики для проверки результатов. Поскольку вам нужно преобразовать только алгоритмический фрагмент в фиксированную точку, более эффективно структурировать ваш код так, чтобы у вас был тестовый файл, в котором вы создаете ваши входы, вызываете ваш алгоритм и строите график результатов, и один (или несколько) алгоритмические файлы, в которых вы делаете основную обработку.

Оригинальный кодЛучшие практикиМодифицированный код
% TEST INPUT
x = randn(100,1);

% ALGORITHM
y = zeros(size(x));
y(1) = x(1);
for n=2:length(x)
  y(n)=y(n-1) + x(n);
end

% VERIFY RESULTS
yExpected=cumsum(x);
plot(y-yExpected)
title('Error')

Проблема

Генерация тестового воздействия и верификация результатов смешиваются с кодом алгоритма.

Зафиксировать

Создайте тестовый файл, который отделен от вашего алгоритма. Поместите алгоритм в свою собственную функцию.

Тестовый файл

% TEST INPUT
x = randn(100,1);

% ALGORITHM
y = cumulative_sum(x);

% VERIFY RESULTS
yExpected = cumsum(x);
plot(y-yExpected)
title('Error')

Алгоритм в собственной функции

function y = cumulative_sum(x)
  y = zeros(size(x));
  y(1) = x(1);
  for n=2:length(x)
    y(n) = y(n-1) + x(n);
  end
end

Можно использовать тестовый файл для:

  • Проверьте, что ваш алгоритм с плавающей точкой ведет себя так, как вы ожидаете, прежде чем преобразовать его в фиксированную точку. Поведение алгоритма с плавающей точкой является базовой линией, с которой вы сравниваете поведение версий вашего алгоритма с фиксированной точкой.

  • Предложите типы данных с фиксированной точкой.

  • Сравните поведение версий алгоритма с фиксированной точкой с базовой линией с плавающей точкой.

Ваши тестовые файлы должны использовать алгоритм в полной рабочей области значений, чтобы области значений симуляции были точными. Для примера, для фильтра, реалистичными входами являются импульсы, суммы синусоидов и щебетание сигналов. С помощью этих входов, используя линейную теорию, можно проверить, что выходы верны. Сигналы, которые дают максимальный выход, полезны для проверки того, что ваша система не переполнена. Качество предлагаемых типов данных с фиксированной точкой зависит от того, насколько хорошо тестовые файлы покрывают рабочую область значений алгоритма с точностью, которую вы хотите.

Подготовьте алгоритм для ускорения кода или генерации кода

Используя Fixed-Point Designer, можно:

  • Инструментируйте код и предоставляйте предложения по типу данных, чтобы помочь вам преобразовать алгоритм в фиксированную точку с помощью следующих функций:

    • buildInstrumentedMex, который генерирует скомпилированный код С, который включает в себя каротажное инструментирование.

    • showInstrumentationResults, который показывает результаты, записанные инструментами, скомпилированными Кодом С.

    • clearInstrumentationResults, который очищает результаты инструментирования из памяти.

  • Ускорите алгоритмы с фиксированной точкой, создав файл MEX с помощью fiaccel функция.

Любые алгоритмы MATLAB, которые вы хотите инструментализировать, используя buildInstrumentedMex и любые алгоритмы с фиксированной точкой, которые вы хотите ускорить, используя fiaccel должен соответствовать требованиям и правилам генерации кода. Для просмотра подмножества языка MATLAB, поддерживаемого для генерации кода, смотрите Функции и Объекты, Поддерживаемые для генерации кода C/C + +.

Чтобы помочь вам идентифицировать неподдерживаемые функции или конструкции в коде MATLAB, используйте один из следующих инструментов.

  • Добавьте %#codegen прагма в верхнюю часть файла MATLAB. Анализатор кода MATLAB помечает функции и конструкции, которые недоступны в подмножестве языка MATLAB, поддерживаемом для генерации кода. Этот совет появляется в режиме реального времени при редактировании кода в редакторе MATLAB.

    Для получения дополнительной информации см. раздел «Проверка кода с использованием анализатора кода MATLAB».

  • Используйте инструмент Генерации кода Readiness, чтобы сгенерировать статический отчет по коду. Отчет определяет вызовы функций и использование типов данных, которые не поддерживаются для генерации кода. Чтобы сгенерировать отчет для функции, myFunction1, в командной строке введите coder.screener('myFunction1').

    Для получения дополнительной информации смотрите Проверить код Используя Инструмент готовности Генерации кода.

Проверяйте на поддержку функций с фиксированной точкой, используемых в вашем алгоритме

Прежде чем вы начнете преобразование с фиксированной точкой, идентифицируете, какие функции, используемые в вашем алгоритме, не поддерживаются для фиксированной точки. Рассмотрим, как вы можете заменить их или иным образом изменить свою реализацию, чтобы быть более оптимизированной для целевых процессоров. Например, вам может потребоваться найти (или написать свои собственные) замены для таких функций, как log2, fft, и exp. Другие функции, такие как sin, cos, и sqrt может поддерживать фиксированную точку, но для большей эффективности, вы можете захотеть рассмотреть альтернативную реализацию, такую как интерполяционная таблица или основанный на CORDIC алгоритм.

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

Оригинальный кодЛучшие практикиМодифицированный код
y = 1/exp(x);

Проблема

exp() функция не задана для входов с фиксированной точкой.

Зафиксировать

Приведите вход к удвоению, пока у вас не будет замены. В этом случае 1/exp(x) больше подходит для роста с фиксированной точкой, чем exp(x), поэтому замените все выражение на 1/exp функцию, возможно, как интерполяционную таблицу.

y = 1/exp(double(x));

Управление типами данных и управление ростом разрядности

Синтаксис (:) = известен как назначение под индексом. При использовании этого синтаксиса MATLAB перезаписывает значение левостороннего аргумента, но сохраняет существующий тип данных и размер массива. Это особенно важно для сохранения фиксированной точки переменных с фиксированной точкой (в отличие от непреднамеренного превращения их в двойные значения) и для предотвращения роста разрядности, когда вы хотите поддерживать конкретный тип данных для выхода.

Оригинальный кодЛучшие практики Модифицированный код
acc = 0;
for n = 1:numel(x)
  acc = acc + x(n);
end

Проблема

acc = acc + x(n)перезаписывает acc с acc + x(n). Когда вы используете все двойные типы, это поведение нормально. Однако, когда вы вводите типы данных с фиксированной точкой в свой код, если acc перезаписан, тип данных acc может измениться.

Зафиксировать

Чтобы сохранить исходный тип данных acc, назначать в acc использование acc(:)=. Использование подписанного назначения приводит значение правой стороны к совпадающий тип данных, что и acc и препятствует росту разрядности.

acc = 0;
for n = 1:numel(x)
  acc(:) = acc + x(n);
end

Для получения дополнительной информации смотрите Управление Ростом разрядности.

Отделите определения типов данных от алгоритма

Для инструментирования и генерации кода создайте функцию точки входа, которая вызывает функцию, которую вы хотите преобразовать в фиксированную точку. Затем можно привести входные параметры функции к различным типам данных. Для сравнения можно добавить вызовы в различные изменения функции. При помощи функции точки входа можно запустить как варианты алгоритма с фиксированной точкой, так и с плавающей точкой. Можно также запустить различные варианты фиксированной точки. Этот подход позволяет вам итерировать на коде быстрее, чтобы прийти к оптимальному проекту с фиксированной точкой.

Этот метод преобразования с фиксированной точкой облегчает для вас сравнение нескольких различных реализаций с фиксированной точкой, а также позволяет вам легко перенастроить алгоритм на другое устройство.

Чтобы отделить определения типов данных от вашего алгоритма:

  1. Когда переменная впервые задана, используйте cast(x,'like',y) или zeros(m,n,'like',y) чтобы привести его к требуемому типу данных.

  2. Составьте таблицу определений типов данных, начиная с исходных типов данных, используемых в вашем коде. Перед преобразованием в фиксированную точку создайте таблицу типов данных, которая использует все отдельные типы данных для поиска несоответствий типов и других проблем.

  3. Запустите код, подключенный к каждой таблице, и проверьте результаты, чтобы проверить подключение.

Оригинальный кодЛучшие практикиМодифицированный код
% Algorithm
n = 128;
y = zeros(size(n));

Проблема

Тип данных по умолчанию в MATLAB является с двойной точностью с плавающей точностью.

Зафиксировать

  1. Использование cast(...,'like',...) и zeros(...'like',...) для программного задания типов, определенных в отдельной таблице.

  2. Создайте исходную таблицу типов, обычно в отдельной функции.

  3. Добавьте в таблицу отдельные типы данных, чтобы помочь проверить соединение с кодом.

% Algorithm
T = mytypes('double');
n = cast(128,'like',T.n);
y = zeros(size(n),'like',T.y);
function T = mytypes(dt)
  switch(dt)
    case 'double'
      T.n = double([]);
      T.y = double([]);
      
    case 'single'
      T.n = single([]);
      T.y = single([]);
  end
end

Отделение спецификаций типа данных от кода алгоритма позволяет вам:

  • Повторно используйте код алгоритма с различными типами данных.

  • Сохраните алгоритм незакрытым со спецификациями типов данных и операторами switch для различных типов данных.

  • Улучшите читаемость кода алгоритма.

  • Переключитесь между типами данных с фиксированной и плавающей точками, чтобы сравнить базовые линии.

  • Переключаться между изменениями настроек с фиксированной точкой, не меняя код алгоритма.

Преобразуйте в фиксированную точку

Каковы ваши цели для преобразования в фиксированную точку?

Прежде чем вы начнете преобразование, рассмотрите свои цели для преобразования в фиксированную точку. Реализуете ли вы свой алгоритм на C или HDL? Каковы ваши целевые ограничения? Ответы на эти вопросы определяют многие свойства с фиксированной точкой, такие как доступный размер слова, длина дроби и математические режимы, а также доступные математические библиотеки.

Создайте и запустите инструментальную MEX-функцию

Создайте и запустите инструментальную MEX-функцию, чтобы получить предложения по фиксированным точкам с помощью buildInstrumentedMex и showInstrumentationResults функций. Тестовые файлы должны использовать ваш алгоритм в полной рабочей области значений. Качество предлагаемых типов данных с фиксированной точкой зависит от того, насколько хорошо тестовый файл покрывает рабочую область значений алгоритма с точностью, которую вы хотите. Простой набор тестовых векторов может не использовать всюсь область значений типов, поэтому используйте предложения как руководство для выбора начального набора фиксированных точек и используйте свое лучшее суждение и опыт в корректировке типов. Если индексы цикла используются только в качестве индексных переменных, они автоматически преобразуются в целые типы, поэтому вы не должны явно преобразовывать их в фиксированную точку.

Код алгоритма Тестовый файл
function [y,z] = myfilter(b,x,z)
  y = zeros(size(x));
  for n = 1:length(x)
    z(:) = [x(n); z(1:end-1)];
    y(n) = b * z;
  end
end
% Test inputs
b = fir1(11,0.25);
t = linspace(0,10*pi,256)';
x = sin((pi/16)*t.^2);  % Linear chirp
z = zeros(size(b'));

% Build
buildInstrumentedMex myfilter ...
  -args {b,x,z} -histogram

% Run
[y,z] = myfilter_mex(b,x,z);

% Show
showInstrumentationResults myfilter_mex ...
  -defaultDT numerictype(1,16) -proposeFL

Составьте таблицу типов

Составьте таблицу типов с помощью структуры с прототипами для переменных. Предлагаемые типы вычисляются из запусков симуляции. Длительный запуск симуляции с широкой областью значений ожидаемых данных дает лучшие предложения. Можно использовать предложенные типы или использовать свои знания об алгоритме и ограничениях реализации, чтобы улучшить предложения.

Поскольку используются типы данных, а не значения, задайте значения прототипа как пустые ([]).

В некоторых случаях может быть более эффективным оставить некоторые части кода в плавающей точке. Для примера, когда существует высокая динамическая область значений или эта часть кода чувствительна к ошибкам округления.

Код алгоритмаТаблицы типовТестовый файл
function [y,z]=myfilter(b,x,z,T)
  y = zeros(size(x),'like',T.y);
  for n = 1:length(x)
    z(:) = [x(n); z(1:end-1)];
    y(n) = b * z;
  end
end
function T = mytypes(dt)
  switch dt
    case 'double'
      T.b = double([]);
      T.x = double([]);
      T.y = double([]);

     case 'fixed16'
      T.b = fi([],true,16,15);
      T.x = fi([],true,16,15);
      T.y = fi([],true,16,14);
  end
end
% Test inputs
b = fir1(11,0.25);
t = linspace(0,10*pi,256)';
x = sin((pi/16)*t.^2); 
% Linear chirp

% Cast inputs
T=mytypes('fixed16');
b=cast(b,'like',T.b);
x=cast(x,'like',T.x);
z=zeros(size(b'),'like',T.x);

% Run
[y,z] = myfilter(b,x,z,T);

Запуск с фиксированными точками и сравнение результатов

Создайте тестовый файл, чтобы подтвердить, что алгоритм с плавающей точкой работает должным образом, прежде чем преобразовывать его в фиксированную точку. Можно использовать тот же тестовый файл, чтобы предложить типы данных с фиксированной точкой и сравнить результаты с фиксированной точкой с базовым уровнем с плавающей точкой после преобразования.

Оптимизация типов данных

Использование масштабированных двойных чисел

Используйте масштабированные двойные значения, чтобы обнаружить потенциальные переполнения. Масштабированные двойки являются гибридом между числами с плавающей точкой и с фиксированной точкой. Fixed-Point Designer сохраняет их как двойные с сохраненной информацией о масштабировании, знаке и размере слова. Чтобы использовать масштабированные двойки, можно использовать свойство override типа данных (DTO) или можно задать 'DataType' свойство к 'ScaledDouble' в fi или numerictype конструктор.

Кому...Использовать...Пример

Локально переопределите тип данных

numerictype DataType свойство

T.a = fi([],1,16,13,'DataType', 'ScaledDouble');
a = cast(pi, 'like', T.a)
a =
    3.1416

   DataTypeMode: Scaled double: binary point scaling
           Signedness: Signed
         WordLength: 16
     FractionLength: 13

Глобально переопределите тип данных

fipref DataTypeOverride свойство

fipref('DataTypeOverride','ScaledDoubles')
T.a = fi([],1,16,13);
a =
  3.1416

  DataTypeMode:Scaled double: binary point scaling
    Signedness: Signed
    WordLength:16
FractionLength:13

Для получения дополнительной информации смотрите Масштабированные двойки.

Используйте гистограмму для точной настройки параметров типа данных

Чтобы настроить настройки типа с фиксированной точкой, запустите buildInstrumentedMex функция со –histogram флаг, а затем запустите сгенерированную MEX-функцию с вашими желаемыми тестовыми воздействиями. Когда вы используете showInstrumentationResults для отображения отчета генерации кода в отчете отображается Иконка гистограммы. Щелкните значок, чтобы открыть NumericTypeScope и просмотреть распределение значений, наблюдаемых в симуляции для выбранной переменной.

Переполнения, обозначенные красным цветом в отчете генерации кода, отображаются в интервале «внешняя область значений» в области NumericTypeScope. Запустите NumericTypeScope для связанной переменной или выражения, нажав на значок представления гистограммы.

Исследуйте компромиссы в проекте

Если у вас есть первый набор типов данных с фиксированной точкой, можно затем добавить различные изменения значений с фиксированной точкой к таблице типов. Можно изменять и итератировать, чтобы избежать переполнения, настроить длины дробей и изменить методы округления, чтобы исключить смещение.

Код алгоритмаТаблицы типовТестовый файл
function [y,z] = myfilter(b,x,z,T)
  y = zeros(size(x),'like',T.y);
  for n = 1:length(x)
    z(:) = [x(n); z(1:end-1)];
    y(n) = b * z;
  end
end
function T = mytypes(dt)
  switch dt
    case 'double'
      T.b = double([]);
      T.x = double([]);
      T.y = double([]);

    case 'fixed8'
      T.b = fi([],true,8,7);
      T.x = fi([],true,8,7);
      T.y = fi([],true,8,6);

    case 'fixed16'
      T.b = fi([],true,16,15);
      T.x = fi([],true,16,15);
      T.y = fi([],true,16,14);
  end
end
function mytest
  % Test inputs
  b = fir1(11,0.25);
  t = linspace(0,10*pi,256)';
  x = sin((pi/16)*t.^2);  % Linear chirp
  
  % Run
  y0  = entrypoint('double',b,x);
  y8  = entrypoint('fixed8',b,x);
  y16 = entrypoint('fixed16',b,x);
  
  % Plot
  subplot(3,1,1)
  plot(t,x,'c',t,y0,'k')
  legend('Input','Baseline output')
  title('Baseline')
         
  subplot(3,2,3)
  plot(t,y8,'k')
  title('8-bit fixed-point output')
  subplot(3,2,4)
  plot(t,y0-double(y8),'r')
  title('8-bit fixed-point error')

  subplot(3,2,5)
  plot(t,y16,'k')
  title('16-bit fixed-point output')
  xlabel('Time (s)')
  subplot(3,2,6)
  plot(t,y0-double(y16),'r')
  title('16-bit fixed-point error')
  xlabel('Time (s)')
end

function [y,z] = entrypoint(dt,b,x)
  T = mytypes(dt);
  b = cast(b,'like',T.b);
  x = cast(x,'like',T.x);
  z = zeros(size(b'),'like',T.x);
  [y,z] = myfilter(b,x,z,T);
end

Оптимизируйте свой алгоритм

Используйте fimath, чтобы получить натуральные типы для C или HDL

fimath свойства определяют правила для выполнения арифметических операций над fi объекты, включая математические, округляющие и переполненные свойства. Можно использовать fimath ProductMode и SumMode свойства для сохранения естественных типов данных для C и HDL. The KeepLSB настройка для ProductMode и SumMode моделирует поведение целочисленных операций в языке C, в то время как KeepMSB моделирует поведение многих устройств DSP. Различные методы округления требуют различных объемов служебного кода. Установка RoundingMethod свойство к Floor, что эквивалентно усечению двух комплементов, обеспечивает наиболее эффективную реализацию округления. Точно так же стандартным методом для обработки переполнений является обертывание с использованием арифметики по модулю. Другие методы обработки переполнения создают дорогостоящую логику. По возможности установите OverflowAction на Wrap.

Код MATLABЛучшие практикиСгенерированный код C
% Code being compiled

function y = adder(a,b)
  y = a + b;
end

With types defined with
default fimath settings:

T.a = fi([],1,16,0);
T.b = fi([],1,16,0);

a = cast(0,'like',T.a);
b = cast(0,'like',T.b);

Проблема

Дополнительный код генерируется для реализации переполнения насыщения, ближайшего округления и полноточной арифметики.

int adder(short a, short b)
{
  int y;
  int i;
  int i1;
  int i2;
  int i3;
  i = a;
  i1 = b;
  if ((i & 65536) != 0) {
    i2 = i | -65536;
  } else {
    i2 = i & 65535;
  }

  if ((i1 & 65536) != 0) {
    i3 = i1 | -65536;
  } else {
    i3 = i1 & 65535;
  }

  i = i2 + i3;
  if ((i & 65536) != 0) {
    y = i | -65536;
  } else {
    y = i & 65535;
  }

  return y;
}

Компилируемый код

function y = adder(a,b)
  y = a + b;
end

С типами, определенными с настройками fimath, которые соответствуют вашим типам процессора:

F = fimath(...
 'RoundingMethod','Floor', ...
 'OverflowAction','Wrap', ...
 'ProductMode','KeepLSB', ...
 'ProductWordLength',32, ...
 'SumMode','KeepLSB', ...
 'SumWordLength',32);

T.a = fi([],1,16,0,F);
T.b = fi([],1,16,0,F);
a = cast(0,'like',T.a);
b = cast(0,'like',T.b);

Зафиксировать

Чтобы сделать сгенерированный код более эффективным, выберите математические настройки с фиксированной точкой, которые соответствуют вашим типам процессора.

int adder(short a, short b)
{
  return a + b;
}

Замените встроенные функции на более эффективные реализации с фиксированной точкой

Некоторые встроенные функции MATLAB могут быть сделаны более эффективными для реализации с фиксированной точкой. Например, можно заменить встроенную функцию реализацией интерполяционной таблицы или реализацией CORDIC, которая требует только итерационных операций shift-add.

Реорганизация операций деления там, где это возможно

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

  • Используйте перемену бит, когда знаменатель является степенью двойки. Для примера, bitsra(x,3) вместо x/8.

  • Умножьте на обратную единицу, когда знаменатель является постоянным. Для примера, x*0.2 вместо x/5.

Устранение переменных с плавающей точкой

Для более эффективного кода устраните переменные с плавающей точкой. Единственным исключением для этого являются индексы цикла, потому что они обычно становятся целыми типами.