プロセッサ・アーキテクチャ

FPU(浮動小数点演算器)

小数計算を高速化する回路。

概要

FPU(Floating Point Unit、浮動小数点演算器)は、プロセッサに内蔵または外付けされる専用回路で、IEEE 754規格に基づく浮動小数点数(小数を含む実数)の演算を高速に処理する。整数演算ユニット(ALU)だけでソフトウェア的に浮動小数点演算を行うと数十〜数百サイクルを消費するが、FPUを使えば多くの演算を1〜数サイクルで完了できる。

組み込みシステムにおいては、センサーの温度・加速度・電圧データの処理、モーター制御のPID演算、音声・画像信号処理など、実数演算が必要な用途でFPUの有無が性能を大きく左右する。かつてはコスト削減のためFPUを省いたマイクロコントローラが主流だったが、近年はARM Cortex-M4/M7/M33以降のミッドレンジMCUにも標準搭載されるようになっている。

FPUが搭載されていない場合、コンパイラはソフトウェア浮動小数点ライブラリ(soft-float)を使って演算を模倣するが、実行速度は大幅に低下し、コードサイズも増加する。FPU搭載環境では「hard-float」オプションでコンパイルすることで、FPUレジスタと命令セットを直接活用できる。

歴史・背景

浮動小数点演算の標準化は1985年にIEEE 754として制定された。それ以前は各メーカーが独自形式を用いており、移植性が低かった。IEEE 754は32ビット単精度(float)と64ビット倍精度(double)の表現形式、丸め規則、特殊値(NaN、無限大)の扱いを定義し、今日の演算の基盤となっている。

デスクトップPC向けでは1980年代にIntel 8087コプロセッサが登場し、x87 FPUとして普及した。組み込み向けではコスト・消費電力の制約から普及が遅れ、ARMがCortex-M4(2010年前後)にオプションとしてFPv4-SP(単精度のみ)を搭載したことが転換点となった。Cortex-M7ではFPv5-DP(倍精度対応)が追加され、ハイエンドMCUでも本格的な浮動小数点処理が可能になった。

RISC-VアーキテクチャはFPUをF拡張(単精度)・D拡張(倍精度)・Q拡張(四倍精度)として標準化しており、実装の選択が容易になっている。Espressif ESP32(Xtensaコア)も単精度FPUを内蔵し、IoTデバイスでの信号処理を支援している。

技術仕様

IEEE 754 浮動小数点形式

種別ビット数符号指数部仮数部十進有効桁
半精度(FP16)161510約3桁
単精度(float)321823約7桁
倍精度(double)6411152約15桁

32ビット単精度floatの値は以下の式で表される。

値 = (-1)^符号 × 2^(指数-127) × (1 + 仮数/2^23)

主要MCUのFPU対応状況

プロセッサFPU精度レジスタ数
ARM Cortex-M0/M0+なし--
ARM Cortex-M3なし--
ARM Cortex-M4FPv4-SP(オプション)単精度32本(S0〜S31)
ARM Cortex-M7FPv5-DP単精度/倍精度32本(D0〜D31)
ARM Cortex-M33FPv5-SP(オプション)単精度32本
ARM Cortex-A53VFPv4+NEON単精度/倍精度64本
RISC-V(F+D拡張)仕様依存単精度/倍精度32本(f0〜f31)
Xtensa LX7(ESP32-S3)FP単精度16本

GCCコンパイルオプション(ARM Cortex-M4F)

# ハードウェアFPU使用(推奨)
arm-none-eabi-gcc -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=hard

# ソフトウェア浮動小数点(FPUなし環境)
arm-none-eabi-gcc -mcpu=cortex-m4 -mfloat-abi=soft

# 混在(soft-float ABI、ハードウェア演算)
arm-none-eabi-gcc -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=softfp

注意: ライブラリとアプリケーションで -mfloat-abi が異なると、リンクエラーまたは実行時クラッシュが発生する。システム全体で統一する必要がある。

動作原理

パイプライン統合

FPUはプロセッサのパイプラインに統合され、整数演算ユニット(ALU)と並列に動作できる設計が多い。ARM Cortex-M4のFPUは、FPADD(加算)・FPMUL(乗算)・FPDIV(除算)・FPSQRT(平方根)などの専用命令を持ち、整数パイプラインとは独立したFPUレジスタファイル(S0〜S31)を使用する。

// C言語でのfloat演算(コンパイラがFPU命令に変換)
float a = 3.14f;
float b = 2.71f;
float result = a * b;  // VMUL.F32命令に対応

// 生成されるARM Thumb-2/FPU命令の例
// VLDR   S0, [r0]     ; S0 = a をロード
// VLDR   S1, [r1]     ; S1 = b をロード
// VMUL.F32 S2, S0, S1 ; S2 = S0 * S1
// VSTR   S2, [r2]     ; 結果を格納

FPUコンテキストとRTOS

RTOSを使用する場合、タスクスイッチ時にFPUレジスタの退避・復元(コンテキストセーブ)が必要になる。ARM Cortex-M4では、FPUを使用するタスクの場合、通常の整数レジスタ(r0〜r3, r12, LR, PC, xPSR)に加えてS0〜S15の浮動小数点レジスタも退避する必要があり、スタック消費量が増加する。

FreeRTOSなど多くのRTOSは configENABLE_FPUportFLOAT_CONTEXT_SIZE などのマクロでFPUコンテキスト管理を設定できる。

// FreeRTOS設定例 (FreeRTOSConfig.h)
#define configENABLE_FPU    1  // FPUコンテキストを有効化
// これにより、タスクスタックにFPUレジスタ分の領域が確保される

遅延コンテキスト保存(Lazy Stacking)

ARM Cortex-Mには「遅延FPUコンテキスト保存」機能がある。FPUを使わないタスクやISRに切り替わる際、FPUレジスタの退避を実際にFPU命令が実行されるまで遅らせることで、不要なスタック操作を削減し割り込み応答時間を短縮する。

用途・ユースケース

センサーデータ処理

温度センサーやIMU(加速度・ジャイロ)から得られる生データは、キャリブレーション係数(float)を掛けて実際の物理量に変換する。FPUがなければこの変換だけで数百サイクルを消費し、高サンプリングレートのセンサー対応が困難になる。

// IMUデータのキャリブレーション(FPU活用例)
typedef struct {
    float scale;
    float offset;
} CalibParam;

float calibrate(int16_t raw, const CalibParam *param) {
    return (float)raw * param->scale + param->offset;
}

// 加速度センサー変換(例: ±2g, 16384 LSB/g)
float accel_g = calibrate(raw_accel, &(CalibParam){1.0f/16384.0f, 0.0f});

PID制御

モーターや温度のPID制御では、比例・積分・微分の各項を浮動小数点で計算する。固定小数点でも実装できるが、スケーリング管理が複雑になるため、FPUがあれば浮動小数点実装が推奨される。

typedef struct {
    float kp, ki, kd;
    float integral;
    float prev_error;
} PIDController;

float pid_update(PIDController *pid, float setpoint, float measured, float dt) {
    float error = setpoint - measured;
    pid->integral += error * dt;
    float derivative = (error - pid->prev_error) / dt;
    pid->prev_error = error;
    return pid->kp * error + pid->ki * pid->integral + pid->kd * derivative;
}

DSP・フィルタ処理

FIRフィルタやIIRフィルタの係数演算、FFT処理にFPUが活躍する。ARM CMSIS-DSP ライブラリはCortex-MのFPUを最大限活用するように最適化されており、float32_t型を使用した信号処理関数群を提供している。

グラフィックス・UI

組み込みGUI(LVGL等)では、アニメーション補間やアンチエイリアシングに浮動小数点演算が使われる。Cortex-M7搭載のSTM32H7などでは、FPUによってスムーズなUI描画が実現できる。

実装・開発のポイント

ビルドシステムの設定確認

最も重要なのはコンパイルオプションの一貫性だ。CMakeを使う場合は以下のように設定する。

# CMakeLists.txt (STM32 Cortex-M4F向け)
set(CPU_FLAGS "-mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${CPU_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CPU_FLAGS}")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${CPU_FLAGS}")

FPU初期化(ベアメタル)

ベアメタル環境では、FPUを使用する前にCP10/CP11コプロセッサへのアクセスを有効化する必要がある。通常はstartupコードに含まれるが、自前で書く場合は以下のように初期化する。

// FPU有効化(Cortex-M4/M7)
void fpu_enable(void) {
    // FPUアクセス許可(CP10, CP11をfull accessに設定)
    SCB->CPACR |= ((3UL << 10*2) | (3UL << 11*2));
    __DSB();  // データ同期バリア
    __ISB();  // 命令同期バリア
}

STM32 HALやArduinoフレームワークを使う場合、このFPU初期化はスタートアップルーティンで自動的に行われる。

soft-floatとhard-floatの混在を避ける

Cコード内で float を使っていても、リンクするライブラリがsoft-floatでビルドされていると実行時に正しく動作しない。特にサードパーティライブラリのABIに注意が必要だ。arm-none-eabi-objdump -d でライブラリの命令セットを確認し、自分のプロジェクトとABIを合わせること。

doubleの使用注意

Cortex-M4のFPUは単精度(float)のみ対応している。double 型の演算はソフトウェア処理になるため、意図せず double を使うとパフォーマンスが低下する。リテラルには必ず f サフィックスを付け、数学関数は sinf(), cosf(), sqrtf() などのfloat版を使う。

// 悪い例: doubleリテラル(soft-float処理になる)
float angle = 3.14159265358979 / 2.0;  // doubleで計算される
float result = sin(angle);             // sin()はdouble版

// 良い例: floatリテラルとfloat関数
float angle = 3.14159265f / 2.0f;     // float計算
float result = sinf(angle);            // sinf()はfloat版

スタックサイズの考慮

FPUを使用するタスクはコンテキストセーブ時により多くのスタックを消費する。RTOSのタスク作成時には、FPUレジスタ(S0〜S31: 64バイト、FPSCR: 4バイト)の分を加算したスタックサイズを確保する。

他技術との比較

項目ハードウェアFPUソフトウェアfloat固定小数点演算
演算速度1〜数サイクル数十〜数百サイクル1〜数サイクル
精度IEEE 754準拠IEEE 754準拠設計依存
コードの複雑さ低い低い高い(スケーリング管理)
コードサイズ大(ライブラリ)小〜中
消費電力中(演算時)中〜大(長時間処理)
移植性アーキテクチャ依存高いアルゴリズム依存

DSPとの違い

DSPは信号処理に特化した演算器で、MAC(乗算累積)演算などを超高速に処理できる。FPUが汎用的な浮動小数点演算器であるのに対し、DSPはSIMD(単一命令複数データ)や専用命令を持ち、特定の信号処理アルゴリズムでははるかに高速に動作する。ARM Cortex-M4はFPUに加えてDSP拡張命令も持つため、軽量なDSP処理もこなせる。

NPUとの違い

NPUは機械学習推論に特化したアクセラレータで、量子化された整数演算(INT8/INT4)で大規模な行列演算を処理する。FPUがfloat演算の汎用器であるのに対し、NPUは特定の計算パターンに高度に最適化されている。エッジAIデバイスではFPU(訓練・前処理)とNPU(推論)を組み合わせて使う構成が一般的だ。

Cortex-MとCortex-Aの違い

ARM Cortex-AはLinuxが動作するアプリケーションプロセッサで、NEON(SIMD)とVFP(倍精度FPU)を標準搭載し、ARM Cortex-Mよりも高度な浮動小数点処理が可能だ。組み込みLinux環境(Raspberry Pi等)ではdoubleを含む演算をハードウェアで高速処理できる。

関連用語

参考リンク