exponenta event banner

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

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

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

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

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

Пример 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 ().

Функция шага вызывает функцию внешнего помощника 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, необходимо вычислить бит знака. Этот бит знака находится в самом значимом слове идеального результата. Кодер снова генерирует два помощника, один для вычисления необработанного 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');

Обратите внимание, что на панели оптимизации диалогового окна конфигурации модели установлен флажок «Optimize using the specified minimum and maximum values» (Оптимизировать с использованием указанных минимальных и максимальных значений).

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

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

Ссылки:

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

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

helpview([docroot '/ecoder/helptargets.map'], 'tfl_registration')
clear ctext fid match
clear ans