Реализуйте алгоритм конечной импульсной характеристики фильтра для с плавающей точкой и Фиксированными точками с помощью приведения и нулей

В этом примере показано, как преобразовать фильтр с конечной импульсной характеристикой (КИХ) в фиксированную точку путем отделения спецификации типа с фиксированной точкой от кода алгоритма.

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

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

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

  • Сохраните код алгоритма более читаемым

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

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

Исходный алгоритм

Этот пример преобразует код MATLAB ® для фильтра с конечной импульсной характеристикой (КИХ) в фиксированную точку.

Формула для n-го выхода y (n) (конечной импульсной характеристики) фильтра, заданного коэффициентами фильтра b и входом x, является:

y(n) = b(1)*x(n) + b(2)*x(n-1) + ... + b(end)*x(n-length(b)+1)

Реализация линейного буфера

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

function [y,z] = fir_filt_linear_buff(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

Вектор z состоит из текущей и предыдущей выборки x:

z = [x(n); x(n-1); .. ; x(n-length(b)+1)]

Вектор z называется вектором состояний. Он используется как вход, так и выход. Когда это вход, это начальное состояние фильтра. Когда это выход, это окончательное состояние фильтра. Определение вектора состояния вне функции позволяет вам постоянно транслировать данные через фильтр в системе реального времени и повторно использовать алгоритм фильтра в рамках того же проекта. Вектор состояний часто называется z, потому что он синонимичен задержкам$z^{-1}$, связанным с Z-преобразованием. Z-преобразование было названо в честь Лотфи Заде за его основополагающую работу в этой области [1].

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

Реализация циклического буфера

Для более эффективной реализации конечной импульсной характеристики можно хранить состояния в круговом буфере z, элементами которого являются z (p) = x (n), где p = mod (n-1, длина (b)) + 1, для n = 1, 2, 3,....

Для примера пусть length (b) = 3, и инициализируют p и z в:

p = 0, z = [ 0    0    0  ]

Начните с первой выборки и заполните буфер z состояния циклическим способом.

n = 1, p = 1, z(1) = x(1), z = [x(1)  0    0  ]
y(1) = b(1)*z(1) + b(2)*z(3) + b(3)*z(2)
n = 2, p = 2, z(2) = x(2), z = [x(1) x(2)  0  ]
y(2) = b(1)*z(2) + b(2)*z(1) + b(3)*z(3)
n = 3, p = 3, z(3) = x(3), z = [x(1) x(2) x(3)]
y(3) = b(1)*z(3) + b(2)*z(2) + b(3)*z(1)
n = 4, p = 1, z(1) = x(4), z = [x(4) x(2) x(3)]
y(4) = b(1)*z(1) + b(2)*z(3) + b(3)*z(2)
n = 5, p = 2, z(2) = x(5), z = [x(4) x(5) x(3)]
y(5) = b(1)*z(2) + b(2)*z(1) + b(3)*z(3)
n = 6, p = 3, z(3) = x(6), z = [x(4) x(5) x(6)]
y(6) = b(1)*z(3) + b(2)*z(2) + b(3)*z(1)
...

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

function [y,z,p] = fir_filt_circ_buff_original(b,x,z,p)
    y = zeros(size(x));
    nx = length(x);
    nb = length(b);
    for n=1:nx
        p=p+1; if p>nb, p=1; end
        z(p) = x(n);
        acc = 0;
        k = p;
        for j=1:nb
            acc = acc + b(j)*z(k);
            k=k-1; if k<1, k=nb; end
        end
        y(n) = acc;
    end
end

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

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

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

Настройка

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

resetglobalfimath;
FIPREF_STATE = get(fipref);
resetfipref;

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

tempdirObj = fidemo.fiTempdir('fir_filt_circ_buff_fixed_point_conversion_example');
copyfile(fullfile(matlabroot,'toolbox','fixedpoint','fidemos','+fidemo',...
                  'fir_filt_*.m'),'.','f');

Фильтрация коэффициентов

Используйте следующий lowpass, которые были вычислены с помощью функции fir1 из Signal Processing Toolbox.

b = fir1(11,0.25);
b = [-0.004465461051254
    -0.004324228005260
    +0.012676739550326
    +0.074351188907780
    +0.172173206073645
    +0.249588554524763
    +0.249588554524763
    +0.172173206073645
    +0.074351188907780
    +0.012676739550326
    -0.004324228005260
    -0.004465461051254]';

Временной вектор

Используйте этот временной вектор, чтобы создать тестовые сигналы.

nx = 256;
t = linspace(0,10*pi,nx)';

Импульсный вход

Реакция конечной импульсной характеристики на импульсный вход - сами коэффициенты фильтра.

x_impulse = zeros(nx,1); x_impulse(1) = 1;

Сигнал, который производит максимальный выход

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

x_max_output = sign(fliplr(b))';
x_max_output = repmat(x_max_output,ceil(nx/length(b)),1);
x_max_output = x_max_output(1:nx);

Максимальная величина выходного сигнала является 1-нормой его импульсной характеристики, которая является нормой (b, 1) = сумма (abs (b)).

maximum_output_magnitude = norm(b,1) %#ok<*NOPTS>
maximum_output_magnitude =

    1.0352

Сумма синусов

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

f0=0.1; f1=2;
x_sines = sin(2*pi*t*f0) + 0.1*sin(2*pi*t*f1);

Щебет

Щебет дает хороший визуал lowpass фильтра действия прохождения низких частот и ослабления высоких частот.

f_chirp = 1/16;                  % Target frequency
x_chirp = sin(pi*f_chirp*t.^2);  % Linear chirp

titles = {'Impulse', 'Max output', 'Sum of sines', 'Chirp'};
x = [x_impulse, x_max_output, x_sines, x_chirp];

Вызовите исходную функцию

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

y0 = zeros(size(x));
for i=1:size(x,2)
    % Initialize the states for each column of input
    p = 0;
    z = zeros(size(b));
    y0(:,i) = fir_filt_circ_buff_original(b,x(:,i),z,p);
end

Выходы базовой линии

fir_filt_circ_buff_plot(1,titles,t,x,y0)

Подготовка к Инструментированию и генерации кода

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

Функция точки входа

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

function y = fir_filt_circ_buff_original_entry_point(b,x,reset)
    if nargin<3, reset = true; end
    % Define the circular buffer z and buffer position index p.
    % They are declared persistent so the filter can be called in a streaming
    % loop, each section picking up where the last section left off.
    persistent z p
    if isempty(z) || reset
        p = 0;
        z = zeros(size(b));
    end
    [y,z,p] = fir_filt_circ_buff_original(b,x,z,p);
end

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

Ваш тестовый файл вызывает скомпилированную функцию точки входа.

function y = fir_filt_circ_buff_test(b,x)
     y = zeros(size(x));
     for i=1:size(x,2)
         reset = true;
         y(:,i) = fir_filt_circ_buff_original_entry_point_mex(b,x(:,i),reset);
     end
end

Создайте исходную функцию

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

reset = true;
buildInstrumentedMex fir_filt_circ_buff_original_entry_point -args {b, x(:,1), reset}

Запуск исходной функции

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

y1 = fir_filt_circ_buff_test(b,x);

Показать типы

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

showInstrumentationResults fir_filt_circ_buff_original_entry_point_mex

Чтобы увидеть эти результаты в отчете генерации кода с инструментами:

  • Выберите функцию fir_filt_circ_buff_original

  • Выберите вкладку Переменные

Проверьте исходную функцию

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

fir_filt_circ_buff_plot2(2,titles,t,x,y0,y1)

Преобразуйте функции в таблицы типов

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

  1. Составьте таблицу определений типов данных.

  2. Измените код алгоритма, чтобы использовать типы данных из этой таблицы.

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

Таблица типов

Составьте таблицу типов с помощью структуры с прототипами для переменных, установленными на их исходные типы. Используйте типы опорной структуры, чтобы подтвердить, что вы правильно сделали начальное преобразование, а также используйте его, чтобы программно переключить функцию между плавающей точкой и фиксированными точками. Индексные переменные j, k, n, nb, nx автоматически преобразуются в целые числа MATLAB Coder™, поэтому вам не нужно задавать их типы в таблице.

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

function T = fir_filt_circ_buff_original_types()
    T.acc=double([]);
    T.b=double([]);
    T.p=double([]);
    T.x=double([]);
    T.y=double([]);
    T.z=double([]);
end

Учитывающая типы функция filter

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

Используйте подписанное назначение acc (:) =..., p (:) = 1, и k (:) = nb, чтобы сохранить типы данных во время назначения. Для получения дополнительной информации о назначении и сохранении типов данных см. раздел «Cast fi Objects» в документации Fixed-Point Designer.

Вызов функции y = zeros (size (x), 'like', T.y) создает массив нулей того же размера, что и x со свойствами переменной T.y. Первоначально T.y является double, заданным в функции fir_filt_circ_buff_original_types, но он переопределяется как тип с фиксированной точкой позже в этом примере.

Вызов функции acc = cast (0, 'like', T.acc) приводит значение 0 с теми же свойствами, что и переменная T.acc. Первоначально T.acc является двойным определением в функции fir_filt_circ_buff_original_types, но он переопределяется как тип с фиксированной точкой позже в этом примере.

function [y,z,p] = fir_filt_circ_buff_typed(b,x,z,p,T)
    y = zeros(size(x),'like',T.y);
    nx = length(x);
    nb = length(b);
    for n=1:nx
        p(:)=p+1; if p>nb, p(:)=1; end
        z(p) = x(n);
        acc = cast(0,'like',T.acc);
        k = p;
        for j=1:nb
            acc(:) = acc + b(j)*z(k);
            k(:)=k-1; if k<1, k(:)=nb; end
        end
        y(n) = acc;
    end
end

Функция точки входа с учетом типа

Вызов функции p1 = cast (0, 'like', T1.p) приводит 0 значения с теми же свойствами, что и переменные T1.p. Первоначально T1.p является двойной функцией, заданной в fir_filt_circ_buff_original_types функции, но она переопределяется как целый тип позже в этом примере.

Вызов функции z1 = нули (size (b), 'like', T1.z) создает массив нулей того же размера, что и b со свойствами переменных T1.z. Первоначально T1.z является двойной функцией, заданной в fir_filt_circ_buff_original_types функции, но она переопределяется как тип с фиксированной точкой позже в этом примере.

function y1 = fir_filt_circ_buff_typed_entry_point(b,x,reset)
   if nargin<3, reset = true; end
   %
   % Baseline types
   %
   T1 = fir_filt_circ_buff_original_types();
   % Each call to the filter needs to maintain its own states.
   persistent z1 p1
   if isempty(z1) || reset
       p1 = cast(0,'like',T1.p);
       z1 = zeros(size(b),'like',T1.z);
   end
   b1 = cast(b,'like',T1.b);
   x1 = cast(x,'like',T1.x);
   [y1,z1,p1] = fir_filt_circ_buff_typed(b1,x1,z1,p1,T1);
end

Проверьте измененную функцию

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

buildInstrumentedMex fir_filt_circ_buff_typed_entry_point -args {b, x(:,1), reset}

y1 = fir_filt_circ_buff_typed_test(b,x);
fir_filt_circ_buff_plot2(3,titles,t,x,y0,y1)

Предложите типы данных из симуляции min/max журналов

Используйте функцию showInstrumentationResults, чтобы предложить длины дробей с фиксированной точкой, учитывая тип с фиксированной точкой по умолчанию и 16-битный размер слова.

showInstrumentationResults fir_filt_circ_buff_original_entry_point_mex ...
    -defaultDT numerictype(1,16) -proposeFL

В инструментальном отчете генерации кода выберите функцию fir_filt_circ_buff_original и вкладку Переменные, чтобы увидеть эти результаты.

Составьте таблицу фиксированных точек

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

Используйте свои знания алгоритма, чтобы улучшить предложения. Например, вы используете переменную acc в качестве аккумулятора, поэтому сделайте его 32-битным. Из отчета генерации кода можно увидеть, что acc нужно как минимум 2 целочисленных бита, чтобы предотвратить переполнение, поэтому установите длину дроби равной 30.

Переменная p используется в качестве индекса, поэтому можно сделать ее 16-битным целым числом builtin.

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

function T = fir_filt_circ_buff_fixed_point_types()
    T.acc=fi([],true,32,30);
    T.b=fi([],true,16,17);
    T.p=int16([]);
    T.x=fi([],true,16,14);
    T.y=fi([],true,16,14);
    T.z=fi([],true,16,14);
end

Добавьте фиксированную точку к функции точки входа

Добавьте вызов в таблицу фиксированных точек в функции точки входа:

T2 = fir_filt_circ_buff_fixed_point_types();
persistent z2 p2
if isempty(z2) || reset
    p2 = cast(0,'like',T2.p);
    z2 = zeros(size(b),'like',T2.z);
end
b2 = cast(b,'like',T2.b);
x2 = cast(x,'like',T2.x);
[y2,z2,p2] = fir_filt_circ_buff_typed(b2,x2,z2,p2,T2);

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

buildInstrumentedMex fir_filt_circ_buff_typed_entry_point -args {b, x(:,1), reset}

[y1,y2] = fir_filt_circ_buff_typed_test(b,x);

showInstrumentationResults fir_filt_circ_buff_typed_entry_point_mex

Чтобы увидеть эти результаты в отчете генерации кода с инструментами:

  • Выберите функцию точки входа, fir_filt_circ_buff_typed_entry_point

  • Выберите fir_filt_circ_buff_typed в следующей строке кода:

[y2,z2,p2] = fir_filt_circ_buff_typed(b2,x2,z2,p2,T2);
  • Выберите вкладку Переменные

16-битный размер слова, полная точность математики

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

fir_filt_circ_buff_plot2(4,titles,t,x,y1,y2);

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

Сгенерируйте код С

В этом разделе описывается, как сгенерировать эффективный код С из кода MATLAB с фиксированной точкой из предыдущего раздела.

Необходимые продукты

Вам нужен MATLAB Coder™, чтобы сгенерировать код С, и вам нужен Embedded Coder ® для настроек аппаратной реализации, используемых в этом примере.

Алгоритм настроен на наиболее эффективный код С

Выходная переменная y инициализируется в нули, а затем полностью перезаписывается перед использованием. Поэтому заполнять y всеми нулями не нужно. Можно использовать функцию coder.nullcopy, чтобы объявить переменную, не заполняя ее фактически значениями, что делает код в этом случае более эффективным. Тем не менее, вы должны быть очень осторожны при использовании coder.nullcopy, потому что, если вы получаете доступ к переменному элементу массива до ее назначения, то вы получаете доступ к неинициализированной памяти и ее содержимое непредсказуемо.

Правило большого пальца для того, когда использовать coder.nullcopy, когда инициализация занимает значительное время по сравнению с остальной частью алгоритма. Если вы не уверены, то самое безопасное - не использовать его.

function [y,z,p] = fir_filt_circ_buff_typed_codegen(b,x,z,p,T)
    % Use coder.nullcopy only when you are certain that every value of
    % the variable is overwritten before it is used.
    y = coder.nullcopy(zeros(size(x),'like',T.y));
    nx = length(x);
    nb = length(b);
    for n=1:nx
        p(:)=p+1; if p>nb, p(:)=1; end
        z(p) = x(n);
        acc = cast(0,'like',T.acc);
        k = p;
        for j=1:nb
            acc(:) = acc + b(j)*z(k);
            k(:)=k-1; if k<1, k(:)=nb; end
        end
        y(n) = acc;
    end
end

Собственные типы кода С

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

Установите математические свойства с фиксированной точкой, чтобы использовать округление пола и переполнение переноса, потому что это действия по умолчанию в C.

Установите математические свойства продуктов и сумм с фиксированной точкой, чтобы соответствовать собственным 32-битным целым типам C и сохранить наименее значимые биты (LSB) математических операций.

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

function T = fir_filt_circ_buff_dsp_types()
    F = fimath('RoundingMethod','Floor',...
               'OverflowAction','Wrap',...
               'ProductMode','KeepLSB',...
               'ProductWordLength',32,...
               'SumMode','KeepLSB',...
               'SumWordLength',32);
    T.acc=fi([],true,32,30,F);
    T.p=int16([]);
    T.b=fi([],true,16,17,F);
    T.x=fi([],true,16,14,F);
    T.y=fi([],true,16,14,F);
    T.z=fi([],true,16,14,F);
end

Протестируйте собственные типы кода С

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

[y1,y2,y3] = fir_filt_circ_buff_typed_test(b,x); %#ok<*ASGLU>

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

fir_filt_circ_buff_plot2(5,titles,t,x,y1,y3);

Масштабированные Double-типы для поиска переполнений

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

Измените типы данных на масштабированные двойные и добавьте эти настройки к таблице масштабируемых двойных типов.

function T = fir_filt_circ_buff_scaled_double_types()
    F = fimath('RoundingMethod','Floor',...
               'OverflowAction','Wrap',...
               'ProductMode','KeepLSB',...
               'ProductWordLength',32,...
               'SumMode','KeepLSB',...
               'SumWordLength',32);
    DT = 'ScaledDouble';
    T.acc=fi([],true,32,30,F,'DataType',DT);
    T.p=int16([]);
    T.b=fi([],true,16,17,F,'DataType',DT);
    T.x=fi([],true,16,14,F,'DataType',DT);
    T.y=fi([],true,16,14,F,'DataType',DT);
    T.z=fi([],true,16,14,F,'DataType',DT);
end

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

[y1,y2,y3,y4] = fir_filt_circ_buff_typed_test(b,x); %#ok<*NASGU>

Отображение результатов инструментирования с типами scaled-double.

showInstrumentationResults fir_filt_circ_buff_typed_entry_point_mex

Чтобы увидеть эти результаты в отчете генерации кода с инструментами:

  • Выберите функцию точки входа, fir_filt_circ_buff_typed_entry_point

  • Выберите fir_filt_circ_buff_typed_codegen в следующей строке кода:

[y4,z4,p4] = fir_filt_circ_buff_typed_codegen(b4,x4,z4,p4,T4);
  • Выберите вкладку Переменные.

  • Посмотрите на переменные в таблице. Ни одна из переменных не переполнена, что указывает на то, что переполнение произошло в результате операции.

  • Наведите на операторов в отчете (+, -, *, =).

  • Наведите на «+» в этой линии кода MATLAB в инструментальном отчете генерации кода:

acc(:) = acc + b(j)*z(k);

Отчет показывает, что сумма переполнена:

Причина переполнения суммы состоит в том, что произведение полной точности для b (j) * z (k) создает численный тип (true, 32,31), потому что b имеет численный тип (true, 16,17) и z имеет числовой тип (true, 16,14). Тип суммы установлен на «сохранение наименьших значащих битов» (KeepLSB), поэтому сумма имеет численный тип (true, 32,31). Однако для хранения минимальных и максимальных моделируемых значений -1.0045 и + 1.035, соответственно, необходимо 2 целочисленных бита.

Настройте, чтобы избежать переполнения

Установите длину дроби b в 16 вместо 17 так, чтобы b (j) * z (k) было числовым типом (true, 32,30), и поэтому сумма также является числовым типом (true, 32,30), следующим за правилом KeepLSB для сумм.

Оставьте все другие настройки одинаковыми и установите

T.b=fi([],true,16,16,F);

Тогда сумма в этой линии кода MATLAB больше не переполнена:

acc(:) = acc + b(j)*z(k);

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

[y1,y2,y3,y4,y5] = fir_filt_circ_buff_typed_test(b,x);

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

fir_filt_circ_buff_plot2(6,titles,t,x,y1,y5);

Устраните смещение

Если смещение неприемлемо в вашем приложении, измените метод округления на 'Nearest', чтобы исключить смещение. Округление к ближайшему генерирует немного более сложный код С, но это может быть необходимо вам, если вы хотите устранить смещение и иметь меньшую ошибку.

Итоговая таблица фиксированных точек с ближайшим округлением и скорректированной длиной дроби коэффициента является:

function T = fir_filt_circ_buff_dsp_nearest_types()
    F = fimath('RoundingMethod','Nearest',...
               'OverflowAction','Wrap',...
               'ProductMode','KeepLSB',...
               'ProductWordLength',32,...
               'SumMode','KeepLSB',...
               'SumWordLength',32);
    T.acc=fi([],true,32,30,F);
    T.p=int16([]);
    T.b=fi([],true,16,16,F);
    T.x=fi([],true,16,14,F);
    T.y=fi([],true,16,14,F);
    T.z=fi([],true,16,14,F);
end

Вызовите эту таблицу типов из функции точки входа и запустите и постройте выход.

[y1,y2,y3,y4,y5,y6] = fir_filt_circ_buff_typed_test(b,x);
fir_filt_circ_buff_plot2(7,titles,t,x,y1,y6);

Команда генерации кода

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

function fir_filt_circ_buff_build_function()
    %
    % Declare input arguments
    %
    T = fir_filt_circ_buff_dsp_nearest_types();
    b = zeros(1,12,'like',T.b);
    x = zeros(256,1,'like',T.x);
    z = zeros(size(b),'like',T.z);
    p = cast(0,'like',T.p);
    %
    % Code generation configuration
    %
    h = coder.config('lib');
    h.PurelyIntegerCode = true;
    h.SaturateOnIntegerOverflow = false;
    h.SupportNonFinite = false;
    h.HardwareImplementation.ProdBitPerShort = 8;
    h.HardwareImplementation.ProdBitPerInt = 16;
    h.HardwareImplementation.ProdBitPerLong = 32;
    %
    % Generate C-code
    %
    codegen fir_filt_circ_buff_typed_codegen -args {b,x,z,p,T} -config h -launchreport
end

Сгенерированный код C

Используя эти настройки, MATLAB Coder генерирует следующий код С:

void fir_filt_circ_buff_typed_codegen(const int16_T b[12], const int16_T x[256],
  int16_T z[12], int16_T *p, int16_T y[256])
{
  int16_T n;
  int32_T acc;
  int16_T k;
  int16_T j;
  for (n = 0; n < 256; n++) {
    (*p)++;
    if (*p > 12) {
      *p = 1;
    }
    z[*p - 1] = x[n];
    acc = 0L;
    k = *p;
    for (j = 0; j < 12; j++) {
      acc += (int32_T)b[j] * z[k - 1];
      k--;
      if (k < 1) {
        k = 12;
      }
    }
    y[n] = (int16_T)((acc >> 16) + ((acc & 32768L) != 0L));
  }
}

Запустите следующий код, чтобы восстановить глобальные состояния.

fipref(FIPREF_STATE);
clearInstrumentationResults fir_filt_circ_buff_original_entry_point_mex
clearInstrumentationResults fir_filt_circ_buff_typed_entry_point_mex
clear fir_filt_circ_buff_original_entry_point_mex
clear fir_filt_circ_buff_typed_entry_point_mex

Запустите следующий код, чтобы удалить временную папку.

tempdirObj.cleanUp;

Ссылки:

[1] Ж. Р. Рагаццини и Л. А. Заде. «Анализ систем выборочных данных». В: Сделки Американского института инженеров-электриков 71 (II) (1952), стр. 225-234.

Для просмотра документации необходимо авторизоваться на сайте