column
技術情報・基礎知識
組み込みソフトウェアの基本 組み込みソフトウェア開発の進め方|要件定義から量産までの全工程と各フェーズの注意点
組み込みソフトウェア開発は、PCアプリやWebサービスの開発と異なり、ハードウェアの完成タイミングや量産工程と歩調を合わせながら進める必要があります。コードを書き始めるまでに整えておくべき前提条件が多く、後工程で発覚した問題ほど修正コストが跳ね上がるという特徴もあります。本記事では、組み込みソフトウェア開発の全工程を要件定義から量産・保守まで順に追いながら、各フェーズで実際に詰まりやすいポイントと、現場で押さえておきたい注意点を解説します。これから新規プロジェクトを立ち上げる方や、ハードウェア設計から組み込み開発に踏み込むことになった方に向けた内容です。
組み込みソフトウェア開発の全体像
組み込みソフトウェア開発は、一般的にV字モデルで語られることが多い領域です。左側の下りで要件定義から詳細設計・実装までを進め、右側の上りで単体テストから結合テスト、システムテストへと検証の粒度を上げていきます。左右が対称になっており、上流で決めた要件が、対応する右側の検証工程で確認されるという構造です。組み込みソフトウェアは出荷後の修正が容易ではないため、ウォーターフォール的な進め方をベースにしつつ、ハードウェアの試作タイミングに合わせて部分的にアジャイル的な反復を組み込むのが現実的な進め方になります。
ここで重要なのは、組み込みソフトウェアの工程が「ソフト単独では完結しない」という点です。要件定義の段階からハードウェア設計、機構設計、調達、品質保証、製造といった他部門と密に連携する必要があり、ソフトウェア工程の遅れがそのまま製品全体の遅れに直結します。各フェーズの目的とアウトプットを明確にし、関係者全員で共有しておくことが、プロジェクトを停滞させないための前提になります。
フェーズ1:要件定義
要件定義は、組み込みソフトウェア開発の土台を固める工程です。ここでの精度が低いと、その後のすべての工程に歪みが波及するため、十分な時間と関係者の巻き込みが必要になります。
要件定義で扱う内容は大きく機能要件と非機能要件に分かれます。機能要件は「何ができる機器か」を定めるもので、操作仕様、入出力仕様、通信プロトコル、表示内容、保存データなどが該当します。非機能要件は「どの程度の性能・品質で動くか」を定めるもので、応答時間、起動時間、消費電力、温度範囲、耐振動性、寿命、規格対応(EMC、機能安全、医療機器規格など)といった要素が含まれます。組み込み機器では非機能要件が製品価値を大きく左右するにもかかわらず、議論が機能要件に偏ってしまうことが多いため、意識的に時間を割く必要があります。
このフェーズで特に注意したいのが次の3点です。
- ハードウェア仕様との同時並行検討(CPU処理能力やメモリ容量はソフト要件と表裏一体)
- タイミング要件の数値化(「速く」ではなく「○○ミリ秒以内」と書く)
- 異常系・例外系の扱い(電源瞬断、通信断、センサー異常時の振る舞いを要件として明記する)
要件定義の段階で「正常系しか書かれていない仕様書」が出てくることがありますが、組み込み機器の信頼性は異常系の振る舞いで決まると言ってよく、ここを曖昧にしたまま次工程に進むと、後で必ず手戻りが発生します。
フェーズ2:基本設計・詳細設計
要件が固まったら、ソフトウェアの構造を設計するフェーズに入ります。基本設計ではシステム全体をモジュールに分割し、各モジュールの役割と相互関係を整理します。詳細設計では、それぞれのモジュール内部の処理を関数レベルまで具体化し、メモリマップ、状態遷移、タスク構成、割り込み構成といった、実装の青写真を作り込みます。
組み込みソフトウェア特有の設計観点として、まず割り込みとタスクの切り分けが挙げられます。リアルタイム性が求められる処理は割り込みハンドラに置くべきか、優先度の高いタスクに置くべきかという判断は、性能と保守性のトレードオフを伴います。割り込みハンドラは応答が速い反面、長く居座ると他の割り込みを取りこぼす危険があり、原則として「短く軽く」が鉄則です。重い処理はフラグを立ててタスク側で処理する、といった分担を設計段階で決めておきます。
メモリマップの設計も重要な検討事項です。ROMとRAMの容量、スタック領域、ヒープ領域、DMA用のバッファ領域などをどう配置するかで、メモリ断片化や予期せぬ領域侵食のリスクが変わります。特にスタックオーバーフローは原因究明が難しい不具合の代表例で、最大スタック使用量を見積もって余裕を持たせる作業は設計フェーズで必ずやっておきたい項目です。
加えて、状態遷移設計とエラー処理設計も避けて通れません。組み込み機器は「電源投入直後」「通常動作中」「エラー復帰中」「シャットダウン中」など複数の状態を持ち、状態間の遷移条件を曖昧にすると、現場で再現困難な不具合の温床になります。状態遷移図を描き、すべての状態と遷移を網羅的に洗い出す作業は、後の検証工程の効率にも直結します。
フェーズ3:実装
設計が固まったら、ようやくコーディングに入ります。組み込みソフトウェアの実装では、C言語が依然として主流で、C++、近年ではRustも選択肢として広がっていますが、いずれの言語を使うにしても、ハードウェア依存層と上位ロジックを明確に分離することが重要です。たとえばGPIOやタイマー、UARTといったペリフェラルへのアクセスを抽象化レイヤーで隠蔽しておけば、マイコンが変わってもアプリケーション層には手を入れずに済み、派生開発や移植時の工数を大幅に削減できます。
コーディング規約も、組み込みでは特に厳格に運用されます。安全性が要求される領域ではMISRA Cが代表的なガイドラインとして用いられ、未定義動作や暗黙の型変換、ポインタの誤用といった、潜在的に危険な書き方を制限します。規約の遵守は静的解析ツールで自動チェックできるため、CIパイプラインに組み込んでおくと、レビュー時の負担を減らしながら品質を担保できます。
実装フェーズで詰まりやすいのが、ハードウェアの仕上がりとソフトウェア進捗のズレです。実機が手に入る前にコードを書き始めるためには、シミュレータや評価ボードを活用したり、ハードウェア依存部をモック化して上位ロジックだけ先に作り込んだりといった工夫が必要になります。実機が来てからすべての検証を始めるのでは、量産までのスケジュールが間に合いません。
フェーズ4:単体テスト・結合テスト
実装が進んだら、検証工程に入ります。組み込みソフトウェアの検証は、粒度の小さいものから大きいものへと段階的に進めるのが基本で、まず単体テストで個々の関数やモジュールの振る舞いを確認し、その後結合テストで複数モジュールが連携した際の動作を確かめます。
単体テストはPC上で実行できることが望ましく、そのためには前述のハードウェア依存部の分離が効いてきます。ハードウェアに触れない上位ロジックは、PC上で自動テストを回せるように設計しておくと、開発スピードと品質が両立しやすくなります。一方、ハードウェアに直接触れる部分は実機やエミュレータでの検証が必要になり、JTAGやICEといったオンチップデバッガ、ロジックアナライザやオシロスコープといった計測機器が活躍します。
結合テストでは、タイミングに起因する不具合や、リソース競合に起因する不具合が表面化しやすくなります。単体では問題なく動くモジュールが、組み合わさると挙動が怪しくなる、というのは組み込み開発では日常的に起こることです。再現性の低い不具合に出会ったときは、ログ出力を仕込みすぎて逆にタイミングが変わってしまう「観測の難しさ」にも向き合うことになるため、デバッグ用の出力経路や、後から有効化できるトレース機能を最初から織り込んでおくと、後工程で助かる場面が多くあります。
フェーズ5:システム評価
ソフトウェア単体としての動作が確認できたら、機器全体としての評価フェーズに進みます。ここでは、機能の最終確認に加え、温度試験、振動試験、EMC試験、長時間連続稼働試験など、製品としての信頼性を確かめる試験が並行して行われます。これらは多くの場合ハードウェア・機構と一体で実施され、ソフトウェア起因の不具合が見つかれば修正と再試験を繰り返すことになります。
このフェーズで初めて顕在化する典型的な問題が、温度や電源変動による異常動作です。常温で動いていたものが高温槽に入れた途端にハングする、低電圧時にだけリセットが正しくかからない、といった不具合は、ラボのデスク上では再現しません。試験で得られたデータを冷静に分析し、原因がソフト側なのかハード側なのかを切り分ける作業は、組み込み開発のなかでも特に経験が問われる場面です。
EMC試験や規格対応の試験は、想定外の改修コストを生みやすい領域でもあります。ノイズ対策のためにソフトウェア側で通信リトライ処理を追加する、安全機能を強化する、といった対応が後から必要になることがあるため、リリース直前にスケジュールを圧迫しないよう、評価フェーズの開始時期を早めに置くのが現場のコツです。
フェーズ6:量産・保守
評価をクリアしたら量産フェーズに移ります。組み込みソフトウェアでは、製造ライン上でマイコンにプログラムを書き込む工程が必ず発生し、書き込み治具・書き込み手順・書き込み後の検査方法を含めて「量産で安定して書ける状態」を作り込む必要があります。書き込みエラーの検出方法、シリアル番号や個体ごとの調整値の管理、書き込みログの保管といった運用面も、量産立ち上げまでに整えておきます。
量産が始まってからも開発は終わりません。フィールドで使われ始めると、想定していなかった使い方や環境に晒され、新たな不具合が見つかることがあります。組み込み機器は出荷後10年以上使われることも珍しくないため、ファームウェア更新の仕組みをどう持つか、不具合発生時にどのように調査・修正・リリースするか、部品EOL(生産中止)が発生したときに代替部品でも同じソフトウェアが動くようにできるか、といった長期保守を見据えた備えが、量産後の運用負荷を大きく左右します。
工程全体を通じて意識したい3つのポイント
最後に、組み込みソフトウェア開発の全工程を通じて意識しておきたい観点を3つ挙げます。
ひとつ目は、ハードウェアと早期から擦り合わせる姿勢です。組み込みソフトウェアの工数や難易度はハードウェアの選定で大きく変わるため、ソフト担当者が回路設計の上流から関わり、メモリ容量やペリフェラルの仕様に意見を出せる体制が望ましいといえます。基板が出来上がってからソフトに渡される、という分業体制は、後工程の手戻りを生む典型的な構図です。
ふたつ目は、異常系と保守性を最初から織り込むことです。正常系だけを優先して作り込み、異常系は後回しにすると、評価フェーズで膨大な手戻りが発生します。状態遷移、エラー処理、ログ取得、ファームウェア更新といった「動かないとき・直すとき」のための仕組みは、最初の設計に組み込んでおくほどコストが下がります。
みっつ目は、関係者間で同じ言葉を使うことです。要件定義書、設計書、テスト仕様書のあいだで用語や粒度がずれていると、レビューや検証の段階で意思疎通の齟齬が生まれ、致命的な見落としに繋がります。プロジェクト初期に用語集と粒度の基準を共有しておくと、工程全体の見通しが格段に良くなります。
手戻りと遅延リスクを低減するための工夫を
組み込みソフトウェア開発は、要件定義からはじまり、設計・実装・検証・評価・量産・保守へと長い工程を辿ります。各フェーズで詰まりやすいポイントは異なりますが、共通して言えるのは、ハードウェアと密に連携し、異常系と長期保守を最初から織り込み、関係者全員で同じ全体像を共有することの重要性です。工程ごとの目的と注意点を押さえたうえでプロジェクトを設計できれば、組み込みソフトウェア開発につきまとう手戻りや遅延のリスクを大きく減らすことができます。