概要
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) | 16 | 1 | 5 | 10 | 約3桁 |
| 単精度(float) | 32 | 1 | 8 | 23 | 約7桁 |
| 倍精度(double) | 64 | 1 | 11 | 52 | 約15桁 |
32ビット単精度floatの値は以下の式で表される。
値 = (-1)^符号 × 2^(指数-127) × (1 + 仮数/2^23)
主要MCUのFPU対応状況
| プロセッサ | FPU | 精度 | レジスタ数 |
|---|---|---|---|
| ARM Cortex-M0/M0+ | なし | - | - |
| ARM Cortex-M3 | なし | - | - |
| ARM Cortex-M4 | FPv4-SP(オプション) | 単精度 | 32本(S0〜S31) |
| ARM Cortex-M7 | FPv5-DP | 単精度/倍精度 | 32本(D0〜D31) |
| ARM Cortex-M33 | FPv5-SP(オプション) | 単精度 | 32本 |
| ARM Cortex-A53 | VFPv4+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_FPU や portFLOAT_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を含む演算をハードウェアで高速処理できる。