組込みソフトウェアの基礎知識
概要
組み込みソフトとは:
家電や自動車、ロボットにはECU基板が内蔵されている。
ECU基板には黒いチップが載っており、それをマイコン(micro controller, micro computer)と呼ぶ。
組み込みソフトウェアはマイコンに埋め込まれるソフトウェアの総称である。
本記事では、ベアメタルな環境を想定。
参考書籍
両方とも名著です。
「CPUの創りかた」は組み込みソフトの書籍ではありませんが、コンピュータの仕組みを知らないと品質の高い組み込みSWは書けません。
- マイコンのしくみと動かし方(トランジスタ技術SPECIAL for フレッシャーズ)
- CPUの創りかた
ALUとinstruction setに関する追加資料
- マイクロコンピュータ入門
CPUの動作
ほとんどのソフトはOSの載った環境でプログラムを動作させるが、組み込みソフトはそうとは限らない。
そのため、コンピュータの仕組みを理解する必要がある。
概要
コンピュータはプログラムを動作させる電子デバイスである。
コンピュータのROMにプログラムを書き込み、順番に動作させることでソフトウェアとして機能する。
コンピュータが動作しているROMの番地を指すレジスタをプログラムカウンタと呼ぶ。
マイコンの構成図を載せる。
動作
コンピュータはFetch, Decode, Executeを繰り返すことで所望の動作を実行している。

プログラム・カウンタ(PC)
次に実行される命令のメモリ・アドレスを保持する特別なレジスタ。
コンピュータが実行するプログラムのアドレスが格納されたレジスタ。
OSがある場合、プログラムはmainから動作するが、ベアメタルの場合、ROMの0番地から実行される。
(OSがROMの0番地から始まっており、ユーザーからはmainから始まっているように見えるだけである。)
Fetch
PCに格納されたアドレスがメモリ・アドレス・レジスタ(MAR)にコピーされ、次に実行される命令のメモリ・アドレスを「指す」ためにPCがインクリメントされる。
フェッチ動作が終了すると、PCは次のサイクルで読み出される次の命令を指す。
次にCPUは、MARによって記述されたメモリアドレスの命令を取り出し、それをメモリデータ・レジスタ(MDR)にコピーする。
MDRはまた、メモリからフェッチされたデータやメモリに格納されるのを待っているデータを保持する双方向レジスタとしても機能する(このため、メモリバッファレジスタ(MBR)とも呼ばれる)。
最終的に、MDR内の命令は、メモリからフェッチされたばかりの命令を一時的に保持する働きをするカレント命令レジスタ(CIR)にコピーされる。

Decode
命令レジスタに示されたエンコードされた命令がデコーダによって解釈される。
コントロールユニット(CU)がCIR内の命令をデコードする。
デコードされた命令はOpcodeとOprandに分けられ、ALUに入力される。
CUは次に、算術論理演算ユニット(ALU)や浮動小数点演算ユニット(FPU)など、
CPU内の他のコンポーネントに信号を送る。
ALUは加算や減算などの算術演算を行うほか、繰り返し加算による乗算や繰り返し減算による除算も行う。
FPUは浮動小数点演算を実行するために予約される。

Execute
CPUの制御ユニットは、デコードされた情報を一連の制御信号としてCPUの関連する機能ユニットに渡し、レジスタから値を読み出したり、その値をALUに渡して数学関数や論理関数を実行させたり、結果をレジスタに書き戻したりするなど、命令で必要とされる動作を実行させる。
ALUが関与している場合、ALUは条件信号をCUに送り返す。
演算によって生成された結果は、メイン・メモリに格納されるか、出力デバイスに送られる。
ALUからのフィードバックに基づいて、PCは次の命令がフェッチされる別のアドレスに更新されることがある。
さらに、ほとんどのプロセッサでは割り込みが発生することがある。
この場合、CPUは割り込みサービスルーチンにジャンプし、それを実行してからリターンする。
場合によっては、命令が途中で割り込まれることがあり、その場合、その命令は何の影響も受けず、割り込みから復帰した後に再実行される。

ALU
operandが入力され、四則演算等の演算を行う。
Instruction Set
命令セット(めいれいせっと、英: instruction set)はプロセッサ命令の集まりである。すなわちコンピュータのハードウェアに対して命令を伝えるための言葉の語彙である。
プロセッサは命令を実行することで処理をおこなう。プロセッサが受け入れ可能な命令はプロセッサごとに異なり、あるプロセッサが受け入れ可能な命令の集合を命令セットという。
各CPUごとに、命令セットアーキテクチャ(ISA:Instruction Set Architecture)が定められており、その中でオペレーションコードが定められている。
opcode(オペコード)の多くは、操作対象のデータを示すoprand(オペランド(被演算子))と組み合わせて使う。
opcode
オペコード (英: opcodeあるいはOpCodeあるいはoperation code) は、操作(operation)に関する命令のコードという意味で、具体的にはプロセッサに与える、操作に関する(機械語の)命令の識別番号[1]。操作対象を示すオペランドと対比される。
操作(operation)に関する命令のコード(code) のためオペレーションコード(operation code)と呼ばれ、通常は略してオペコード(opcode, OpCode)と呼ばれている。
oprand
演算子が意味する演算の対象(数学的対象・あるいは演算可能な量など)
1+1の場合、"+"がopcodeで、"1"がoprandである。
(デバッガコマンドでレジスタの値全てを出力するコマンドがあり、opcode、oprandが含まれていたはず。)
16bitマイコン、32bitマイコン
instruction setの大きさのことである。
(やっぱりジム・ケラーは天才。)
RISC, CISC
CISC(シスク)、RISC(リスク)とは、命令セットアーキテクチャの設計手法を指す言葉。
命令の仕方の違いを表す。
- CISC:Complex Instruction Set Computer(「複雑命令セットコンピュータ」)
- RISC:Reduce Instruction Set Computer(「縮小命令セットコンピュータ」)
(最近の流行りはRISC-V)
CISC
複雑命令とは1つの命令が一連の複雑な処理を実行する方式。
CISCは、いろいろな処理をできるだけ少ない命令回数で済ませることで、マイコンのパフォーマンスを上げる演算方式。
RISC
縮小命令は1つの命令が簡単な処理しか行わない命令。
簡単な命令の分、1つ1つの命令は高速に実行される。
複数の縮小命令を高速に実行することで、トータルでマイコンのパフォーマンスを上げるという演算方式。
マイコンの動作
レジスタ
フリップフロップから構成される。
フリップフロップは制御入力が有効の時のみ出力が更新される回路である。
マイコンをリセットした際、レジスタの値が保持されることに注意する必要がある。
例えば、モータを回転させるパルスを出力していた場合、誤ってデバッガを抜いてしまうと、
レジスタ値は保持され、パルスは出力され続け、モータは回転し続ける。
以下のように全ての出力に対して制御入力Eがあるのではないため、数Byte単位でないとアクセスできないレジスタもある。(以下はD-FF)

スタック
データやアドレスの一時的な保存領域として使用されるメモリ構造。
データの追加(プッシュ)と削除(ポップ)を効率的に行う「後入れ先出し」(Last-In, First-Out; LIFO)というアクセス方法に従う。
スタックは、関数の呼び出しや割込み処理でのアドレスの復元等に利用され、プログラムの制御フローを管理するのに役立つ。
さらに、局所変数や一時的な計算結果の格納にも使われる。スタックは組み込みシステムにおいて重要な役割を担っており、リソースが限られているため適切な管理が不可欠である。
割込み発生時のプログラムカウンタ、関数コール前の関数のローカル変数、制御レジスタに入り切らなかったローカル変数等が格納される。
スタックポインタ
プログラムのスタック領域の現在のトップを指すレジスタ。
関数呼び出しや局所変数の確保・解放時に動的に変更される。
不正なアクセスやオーバーフローを防ぐために重要な役割を果たす。

スタックオーバーフロー
スタックを積み過ぎて、スタック領域を超えてしまい、変数領域を上書きしてしまうこと。
関数をコールすると制御レジスタに入りきらない分はスタックに積まれる。
再帰関数を実装すると関数がコールする度にローカル変数がスタックに積まれるので、ベアメタルにおいて再帰関数は要注意。
割込み
外部イベントやタイマーによって発生し、優先度の低いプログラムの進行を一時停止させて割込み処理を実行する仕組み。
プログラムカウンタは、次に実行される命令のアドレスを格納する。
割込みが発生すると、まず現在のプログラムカウンタの値がスタックに保存される。
割込みハンドラ(割込み処理)のアドレスがプログラムカウンタに設定され、割込み処理が実行される。
割込み処理が完了した後、スタックから元のプログラムカウンタの値が取り出され、プログラムカウンタに復元されて、メインプログラムが中断された箇所から実行が再開される。

ベクターテーブル
割り込みや例外処理が発生した際にCPUがジャンプするアドレスを格納したテーブル。
組み込みシステムではリアルタイム処理が重要なため、このテーブルが高速にアクセスされる。
各エントリは特定のイベント(例:タイマー割り込み、I/O割り込み)に対応している。
マイコンの機能
マイコンには多くの機能が搭載されており、組み合わせることで様々な機能が実現できる。
GPIO
ポートの電圧をHi, Lowさせたり、ポートの電圧を読み込む機能。
LEDを点滅させたり、スイッチが押されたことを検出することができる。
LEDの点灯回路
LEDはマイコン側をLowで光らせる。
マイコンは電流を引き込む限界の方が通常大きいためである。
ウォッチドッグタイマ(WDT)
周期処理とGPIOを組み合わせて実行する。
最も優先度の低いメイン周期で行う。
優先度の高い割込みで行うと他の割込みが停止していてもリセットできないためである。
WDTは電源ICの機能として提供されていることが多い。
タイマ
一定周期を計算したり、時間を計測することができる。
HWタイマとSWタイマがあり、ここではHWタイマに関して説明する。
GPIOとタイマの機能を組み合わせるとPWM出力を生成したり、パルス幅や周期を計測できる。
PWM(Pulse Width Modulation)
パルスを出力する機能。
モータを回転させたり、LEDの照度を調整することができる。
モータを回転させるためには、ドライブICやIGBT等を用いて高電圧・高電流に耐えうる回路構成が必要である。
ICU(Input Capture Unit)
外部信号(通常はディジタル)のタイミングや周期を精密に測定するための機能。
特定のピンでのエッジ(立ち上がりまたは立ち下がり)を検出する。
エッジの瞬間のタイマー値をキャプチャし、それを用いて信号の周期やパルス幅を計算できる。
エンコーダー読み取り、周波数測定、PWM信号解析など、多くの組み込みアプリケーションで用いられる。
AD変換
以下のようにポート電圧を読み取ることができる。
- ポートに入力された電圧はサンプルホールド回路のキャパシタに電荷を貯める。
(瞬間的な電圧が印加されても、キャパシタに電荷が貯まった後でないと正しくAD変換できないことに注意) - AD変換開始タイミングでサンプルホールド回路のスイッチがOFFになり外部の電圧が遮断される。
- 比較信号生成器でカウントアップする。
- カウントアップした値をDA変換器でアナログ信号として出力する。
- キャパシタの電位とアナログ信号をコンパレータで比較する。
- キャパシタの電位をアナログ信号が越えるとコンパレータの出力論理が反転する。
- その時の比較信号生成器のカウント値がAD変換結果レジスタに格納される。
上記は、逐次比較式のAD変換であり、二分探索によるAD変換もある。
計算量はO(n)とO(logn)であり、二分探索の方がAD変換時間が短い。
逐次比較式のAD変換器がほとんどである。

通信
マイコンによりサポートしているプロトコルが異なる。
RS-232/422/485, SPI、CAN、LIN、Ethernetと言ったプロトコルが使わることが多い。
外部ICを必要とするため、外部ICの仕様書を読むことも組み込みエンジニアの仕事である。
CANやLINのプロトコルに関しては
link
を参照すること。
実装方法には工夫が必要であり、単純に受信完了割込みから受信バッファに格納するのは下策である。
受信完了ステータスレジスタやダブルバッファを用いることで組み込み特有のバグを予防できる。
割込み
様々な割り込みの種類があり、用途に応じて使い分ける必要がある。
割込みを使えば、機能を簡単に実装できるが、増やすほど予期せぬ動作に繋がることがあるため、可能な限り割込み数は少ない方が良い。
マイコン自体の不具合やコンパイラのバグ等の未知の異常に対する予防が必要である。
SWタイマを用いたり、受信完了フラグレジスタを用いることで割込み数を減らすことができる。
具体例な割込みの設定方法
以下のようなレジスタ設定にすることで、マイコンのHW処理が完了待ちの処理を減らしたり、割り込み数を減らすことができる。
- モータ速度計測:100usタイマ→HWトリガ→速度計測→DMA転送→DMA転送完了割込み
- モータ制御:キャリア生成→HWトリガ→AD変換→AD変換完了割込み
DMA
DMA(Direct Memory Access)は、CPUを介さずにメモリとI/Oデバイス間でデータを直接転送する手法。
組み込みシステムでは、高速なデータ処理やリアルタイム操作が求められる場合によく用いられる。
DMAコントローラは、CPUが他のタスクに集中できるように、データ転送を自動的に管理できる。
データフラッシュ
マイコンの電源が消えても記憶したいデータを保存する機能。
キャリブレーション値や異常ログを残す。
異常ログを20個残せるとしたら、最初の10個を起きた順に残して、後半の10個をリングバッファで残す等の工夫をすると異常を解析する際の助けになることが多い。
コードフラッシュ
コードが書かれる領域。
ECC
宇宙線の影響によりRAMが書き変わってしまうことを検出し、復元やリセットを行う。
- 1bit誤り:復元
- 2bit誤り:リセット
アセンブラ言語
基本的にはC言語で記述するが、一部の初期化やマイコン機能を用いる際にアセンブラ言語を用いる。
- RAM初期化:起動時にRAMの初期化を行うが、C言語で書くと、その処理に初期化していないスタック領域のRAMを用いてしまう。そのため、アセンブラ言語を用いて、汎用レジスタを用いて初期化を行う。
- クロック設定:RAM初期化を高速で行うためにクロックを早くする。
- 特殊なマイコン機能:マイコンのソフトウェアマニュアルにどう書けば良いか書いてある。
参考サイト
CPU関連
- https://en.wikipedia.org/wiki/Instruction_cycle
- http://bucarotechelp.com/computers/architecture/86080001.asp
Discussion