Main Content

生成されたコード内の固定小数点乗算補助関数

この例では、生成コード内の乗算補助関数の生成を制御する方法を示します。

この例では、次の内容について説明します。

  • 補助関数の生成環境

  • 補助関数の除去方法

例 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);

2 つの補助関数 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 で補助関数が生成された理由

2 つの 32 ビット数を乗算すると、64 ビットの理想的な結果が得られます。例 1 では、理想的な結果の型は uint64 です。

実際の出力型である ufix32_E5 では、これらの 64 ビットのうちの 5 ~ 36 ビットのみが使用されます。

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

出力データ型には、理想的な乗算型の最上位ワードおよび最下位ワードのビットが含まれています。入力データ型および出力データ型がシングル ワードの場合も理想的な結果の作成に 2 ワードが使用され、出力型に変換されます。補助関数は、幅広の理想的な乗算を操作するために生成されます。

2 つの補助関数がある理由

2 ワードの乗算とそのキャストの組み合わせは、2 つの補助関数でエンコードされます。内部関数 mul_wide_u32() は 2 つの 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 によって 2 つの補助関数が生成されます。1 つは生の 64 ビットの結果を計算し、もう 1 つは飽和キャストを実行します。

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 マクロまたはカスタム関数で置き換えて、コード生成をカスタマイズすることができます。幅の広い乗算をより効率的に実装するためのリソースを CPU が備えている場合などにこの操作を行う場合があります。

参考文献:

clear ctext fid match
clear ans