Функции помощника умножения с фиксированной точкой в сгенерированном коде

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

В этом примере вы узнаете:

  • При каких обстоятельствах генерируются эти помощники

  • Какие подходы доступны для их устранения

Пример 1 - Функция помощника из-за области значений выходов

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

open_system('fxpdemo_mulhelpers_example1');
set_param('fxpdemo_mulhelpers_example1', 'SimulationCommand', 'Update');

Сгенерируйте код для модели и проверьте его:

evalc('slbuild(''fxpdemo_mulhelpers_example1'');'); % Suppress output
fid = fopen('fxpdemo_mulhelpers_example1_grt_rtw/fxpdemo_mulhelpers_example1.c') ; ctext = fread(fid, '*char')'; fclose(fid);

Были сгенерированы две вспомогательные функции: mul_wide_u32 () и mul_u32_loSR ().

Функция step вызывает внешнюю вспомогательную функцию mul_u32_loSR ():

match = regexp(ctext, 'void fxpdemo_mulhelpers_example1_step.*?\n\}', 'match'); disp(match{1});
void fxpdemo_mulhelpers_example1_step(void)
{
  /* Product: '<Root>/MulHelper1' incorporates:
   *  Inport: '<Root>/In1'
   *  Inport: '<Root>/In2'
   */
  Y = mul_u32_loSR(U1, U2, 5U);
}

Функция внешнего помощника вызывает функцию внутреннего помощника mul_wide_u32 ():

match = regexp(ctext, 'uint32_T mul_u32_loSR.*?\n\}', 'match'); disp(match{1});
uint32_T mul_u32_loSR(uint32_T a, uint32_T b, uint32_T aShift)
{
  uint32_T result;
  uint32_T u32_chi;
  mul_wide_u32(a, b, &u32_chi, &result);
  return u32_chi << (32U - aShift) | result >> aShift;
}

Внутренняя вспомогательная функция mul_wide_u32 () также сгенерирована в файле:

match = regexp(ctext, 'void mul_wide_u32.*?\n\}', 'match'); disp(match{1});
void mul_wide_u32(uint32_T in0, uint32_T in1, uint32_T *ptrOutBitsHi, uint32_T
                  *ptrOutBitsLo)
{
  uint32_T in0Hi;
  uint32_T in0Lo;
  uint32_T in1Hi;
  uint32_T in1Lo;
  uint32_T outBitsLo;
  uint32_T productHiLo;
  uint32_T productLoHi;
  in0Hi = in0 >> 16U;
  in0Lo = in0 & 65535U;
  in1Hi = in1 >> 16U;
  in1Lo = in1 & 65535U;
  productHiLo = in0Hi * in1Lo;
  productLoHi = in0Lo * in1Hi;
  in0Lo *= in1Lo;
  in1Lo = 0U;
  outBitsLo = (productLoHi << 16U) + in0Lo;
  if (outBitsLo < in0Lo) {
    in1Lo = 1U;
  }

  in0Lo = outBitsLo;
  outBitsLo += productHiLo << 16U;
  if (outBitsLo < in0Lo) {
    in1Lo++;
  }

  *ptrOutBitsHi = (((productLoHi >> 16U) + (productHiLo >> 16U)) + in0Hi * in1Hi)
    + in1Lo;
  *ptrOutBitsLo = outBitsLo;
}

Почему была сгенерирована вспомогательная функция для примера 1?

Умножение двух 32-битных чисел приводит к 64-битному идеальному результату. В примере 1 тип идеального результата - uint64.

Фактический выход ufix32_E5 использует только биты 5-36 из этих 64 битов.

bits = 0:63;
ideal_product_bits = ones(1,64);
most_significant_word = [ones(1,32) NaN*ones(1,32)];
least_significant_word = [NaN*ones(1,32) ones(1,32)];
output_bits = [NaN*ones(1, 27) ones(1,32) NaN*ones(1,5)];

plot(bits, ideal_product_bits, '.r');
set(gca, 'YLim', [-1 5]);
set(gca, 'YTick', 1:3);
set(gca, 'YTickLabel', {'ideal result', 'MSB | LSB', 'output data type'});
set(gca, 'XTick', 1:64);

% "Move" interesting tick labels so they're readable and show
set(gca, 'xTickLabel', {'63','','','','','','','',  '','','','','','','','', ...
                          '','','','','','','','',  '','','','','','','32','', ...
                          '31','','','','','','','',  '','','','','','','','', ...
                          '','','','','','','','',  '','','','','','','','0'});
set(get(gca,'XLabel'),'String','Bit positions')
hold on
plot(bits, most_significant_word*2, '.b');
plot(bits, least_significant_word*2, '.m');
plot(bits, output_bits*3, '.g');
legend('ideal result', 'most significant word', 'least significant word', 'output ufix32\_E5') % Escape _ to avoid subscripting the E
plot([31.5 31.5], [0 4]); % MSB-LSB boundary
clear bits ideal_product_bits most_significant_word least_significant_word output_bits

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

Почему существуют две вспомогательные функции?

Комбинация двойного умножения слов и приведения кодируется в двух вспомогательных функциях. Внутренняя функция mul_wide_u32 () выполняет длинное умножение двух 32-битных операндов на 64-битный результат. Внешняя функция mul_u32_loSR () выполняет преобразование типа данных из необработанного результата uint64 в желаемый тип выходных данных.

close_system('fxpdemo_mulhelpers_example1', 0);
close all;

Пример 2: Вспомогательные функции из-за насыщения

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

open_system('fxpdemo_mulhelpers_example2');
set_param('fxpdemo_mulhelpers_example2', 'SimulationCommand', 'Update');

evalc('slbuild(''fxpdemo_mulhelpers_example2'');'); % Suppress output
fid = fopen('fxpdemo_mulhelpers_example2_grt_rtw/fxpdemo_mulhelpers_example2.c') ; ctext = fread(fid, '*char')'; fclose(fid);
match = regexp(ctext, 'void fxpdemo_mulhelpers_example2_step.*?\n\}', 'match'); disp(match{1});
void fxpdemo_mulhelpers_example2_step(void)
{
  /* Product: '<Root>/MulHelper1' incorporates:
   *  Inport: '<Root>/In1'
   *  Inport: '<Root>/In2'
   */
  Y = mul_us32_sat(U1, U2);
}

Эта модель не имеет проблемы области значений, которую имела модель в примере 1. Тип выхода соответствует наименее значимому слову идеального продукта.

В этой модели идеальный тип может быть отрицательным, но выход не подписан. Чтобы насыщать отрицательные значения до 0, бит знака должен быть вычислен. Этот бит знака находится в самом значимом слове идеального результата. Coder снова генерирует два помощника, один для вычисления необработанного 64-битного результата и один для выполнения насыщающего приведения.

close_system('fxpdemo_mulhelpers_example2', 0);

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

Возможно избежать генерации помощников умножения путем:

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

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

  • Умножение небольших типов данных на одно слово идеальный продукт

open_system('fxpdemo_mulhelpers_example3');
set_param('fxpdemo_mulhelpers_example3', 'SimulationCommand', 'Update');

evalc('slbuild(''fxpdemo_mulhelpers_example3'');'); % Suppress output
fid = fopen('fxpdemo_mulhelpers_example3_grt_rtw/fxpdemo_mulhelpers_example3.c') ; ctext = fread(fid, '*char')'; fclose(fid);
match = regexp(ctext, 'void fxpdemo_mulhelpers_example3_step.*?\n\}', 'match'); disp(match{1});
void fxpdemo_mulhelpers_example3_step(void)
{
  /* Product: '<Root>/Mul1' incorporates:
   *  Inport: '<Root>/In1'
   *  Inport: '<Root>/In2'
   */
  Y1 = (U1 * U2) << 3;

  /* Product: '<Root>/Mul2' incorporates:
   *  Inport: '<Root>/In3'
   *  Inport: '<Root>/In4'
   */
  Y2 = (uint16_T)(((uint32_T)U3 * U4) >> 5);
}
close_system('fxpdemo_mulhelpers_example3', 0);

Избегание функций помощника путем определения областей значений проекта

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

Примечание.Для этой функции требуется лицензия Embedded Coder.

Давайте рассмотрим модель, подобную модели в примере 1:

open_system('fxpdemo_mulhelpers_example4');
set_param('fxpdemo_mulhelpers_example4', 'SimulationCommand', 'Update');

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

get_param(bdroot, 'UseSpecifiedMinMax')
ans =

    'on'

Исследуйте функцию шага в сгенерированном коде:

evalc('slbuild(''fxpdemo_mulhelpers_example4'');'); % Suppress output
fid = fopen('fxpdemo_mulhelpers_example4_ert_rtw/fxpdemo_mulhelpers_example4.c') ; ctext = fread(fid, '*char')'; fclose(fid);
match = regexp(ctext, 'void fxpdemo_mulhelpers_example4_step.*?\n\}', 'match'); disp(match{1});
void fxpdemo_mulhelpers_example4_step(void)
{
  /* Product: '<Root>/Mul1' incorporates:
   *  Inport: '<Root>/In1'
   *  Inport: '<Root>/In2'
   */
  Y = (U1 * U2) >> 5;
}

Область значений возможных значений идеального продукта составляет от 0 до 500. Для выражения этого продукта достаточно одного типа данных слов.

close_system('fxpdemo_mulhelpers_example4', 0);

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

Отказ от вспомогательных функций с помощью замены оператора CRL

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

Ссылки:

  • Создание таблиц замещения кода:

helpview([docroot '/ecoder/helptargets.map'], 'tfl_mapi')
  • Библиотеки замещения кода регистра:

helpview([docroot '/ecoder/helptargets.map'], 'tfl_registration')
clear ctext fid match
clear ans
Для просмотра документации необходимо авторизоваться на сайте