В этом примере показано, как управлять созданием вспомогательных функций умножения в сгенерированном коде.
В этом примере вы узнаете:
При каких обстоятельствах создаются эти помощники
Какие подходы имеются для их устранения
Следующая модель создает вспомогательные функции для умножения, поскольку диапазон идеального произведения превышает диапазон одного типа данных слова.
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;
}
Умножение двух 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;
Следующая модель создает вспомогательные функции для умножения, чтобы реализовать насыщение.
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);
Внимание: Если при выполнении сгенерированного кода значения сигналов превышают проектные диапазоны, могут быть вычислены неверные результаты.
Генерацию кода можно настроить путем замены громоздких операторов умножения собственными макросами Си или пользовательскими функциями. Вы можете сделать это, если, например, у вашего ЦП есть ресурсы для более эффективной реализации широкого умножения.
Ссылки:
Создание таблиц замены кода:
helpview([docroot '/ecoder/helptargets.map'], 'tfl_mapi')
Библиотеки замены кодов регистров:
helpview([docroot '/ecoder/helptargets.map'], 'tfl_registration')
clear ctext fid match clear ans