仕事関連でARMのCPUを触ることが多いのですが、アセンブラや命令セットなど中々わかっていないことが多いので、自分用のメモとしてここに残しておきます。
ARMマイコンとは
ARM社が設計したRISCマイクロプロセッサのアーキテクチャ(ARMアーキテクチャ)で作られたマイコン。
組み込み開発用に作られており、バッテリーなどを用いて動くことが想定されているため、消費電力・発熱量を抑えることや低コストであることを重視して作られている
バス
バスとは、CPUと主記憶装置などの内部装置とのやりとりに使うための伝送経路のこと。バスの設計方式に種類があり、バスクロックとプロセッサクロックの速度の違いを考慮する必要がある。バス方式とクロックについて以下に記す。
バス方式
バス方式にはフォンノイマン型とハーバード方式がある。
フォンノイマン型ではデータバスと命令バスが同じになっており、データが頻繁にアクセスされると命令が実行できないという問題(フォンノイマンボトルネック)がある。
ハーバード方式は、データバスと命令バスが分かれており、このフォンノイマンボトルネックを解消している。ARMのcortex-Mシリーズもハーバード方式を採用している
プロセッサクロックとバスクロック(dsb,isb命令について)
プロセッサクロックと比べ、バスクロックは低速なため、プロセッサがメモリに書き込むまで待機するのは非効率である。よって、プロセッサ実行速度の低下を防ぐため、プロセッサとバスをつなぐためのライトバッファというものが存在する。ライトバッファはFIFO(First in First out)で動く。
このライトバッファの制御に関して、以下のような命令を用いる
dsb
- dsbより前にあるメモリアクセスが終了するまで待機する。要するにライトバッファ内にあるメモリアクセスの命令がすべて完了するまでここで待つ
isb
- パイプライン上にある命令を破棄する。破棄された命令はメモリシステムから再度フェッチされることになる
命令セット
ARMのCPUの命令セットには、ARM命令セット、Thumb命令セット、Thumb-2命令セットの3つがあり、多くのマイコンではThumb命令、Thumb-2命令が使われます。
ARM命令セット
32ビット幅の命令セット。32ビット幅16本の汎用レジスタと使うことができる
Thumb命令セット
基本命令長を16bit化した命令セット。cortex-M0,M0+で使用されている
Thumb-2命令セット
16bitと32bitを混在させた命令セット。例えばcortex-M3ではこの命令セットを採用している。
16bit幅のThumb命令2個分を使って32ビット1命令分をコードするような拡張をThumb命令に追加したものとなる。
レジスタ
msp(メインスタックポインタ)
psp(プロセススタックポインタ)
- msp,pspは共にスタックポインタで、スレッドモード、ハンドラモードでどちらが使われるかが変わる
- スレッドモードではcontrolレジスタの値によってどちらが使われているかを示します。ハンドラモードではmspを使用
-
controlレジスタを0クリアすると、スレッドモードがユーザモードとして起動する(pspを使用)
-
スレッドモードではプログラムステータスレジスタのSPSELビットでも指定できる
-
リンクレジスタ
- BX,BLX命令を実行したい際に、PCの値がLRに転送される。割り込み時は、割り込みからの復帰を示すため、EXC_RETURNコードがリンクレジスタに入っている
プログラムステータスレジスタ(xPSR)
- APSR
- 特権モード以外で使用するフラグ等が入る
- IPSR
- 現在処理中の割り込み番号が格納されている
- EPSR
- 中断が発生した箇所から複数ロードまたは複数ストアを継続するために必要な情報が保持されている
割り込み
- IRQは通常割り込み、FIQは高速割り込みを示す
- FIQはベクタテーブルの最後のエントリー(7番目)となっている。このため割り込みハ
ンドラーをこの位置に配置することで即実行でき、ジャンプ命令分の処理時間を短縮す
ることができる。 - FIQは複数の割り込みが起こった際に優先して処理される
- FIQはベクタテーブルの最後のエントリー(7番目)となっている。このため割り込みハ
割り込みマスクレジスタ
-
割り込み・例外の発生の有無を操作する。
-
PRIMASKレジスタ, FAULTMASKレジスタ, BASEPRIレジスタがそれにあたる
-
MRC,MCR命令でアクセス
-
プロセッサがスレッドモードにあり、割り込みマスクレジスタが設定されていない
状態を、「ベースレベル」とよび、これが初期状態となる。ベースレベルでの割り込み
の優先度は256(最低値)となっており、全ての割り込みが許可されている- この割り込み優先度は割り込みマスクレジスタで変更可能。
- PRIMASKレジスタの値が1ならば、現在の優先度は1となり、それよりも低い値
の優先度(ハードフォルト、NMI, リセット)のみ受け付ける - FAULTMASKレジスタの値が1ならば、現在の優先度は-1となり、NMI,リセット
のみを受け付ける - BASEPRIレジスタが0以外の値Xを取る場合、Xより低い値の優先度の割り込み
を受け付ける
- PRIMASKレジスタの値が1ならば、現在の優先度は1となり、それよりも低い値
- この割り込み優先度は割り込みマスクレジスタで変更可能。
-
cpsid f
- FAULTMASKレジスタの値を1にする命令(ハードフォルト、NMI、リセット以外の割り込みを禁止)
cpsie f
- FAULTMASKレジスタの値を0にする命令(ハードフォルト、NMI、リセット以外の割り込みを許可)
cpsid i
- PRIMASKレジスタの値を1にする命令(ハードフォルト、NMI、リセット以外の割り込みを禁止)
cpsie i
- PRIMASKレジスタの値を0にする命令(割り込みを許可)
割り込みの手順
- ベクタ・フェッチ(ハードウェア処理)
- コンテキスト(プログラムカウンタ(PC),プログラスステータスレジスタ(xPSR),R0~R3,R12,LR(R14))をスタックに退避(ハードウェア処理)
- LRにEXC_RETURNコードを格納する(ハードウェア処理)
- 割り込みハンドラで破壊するレジスタをスタックに退避(ソフトウェア処理)
- 例外処理(ソフトウェア処理。ここを主にユーザが実装する)
- 割り込みハンドラで破壊するレジスタをスタックから回復(ソフトウェア処理)
- 「BX LR」を実行(ソフトウェア処理)
- 手順2で退避したコンテキストをスタックから回復(ハードウェア処理)
- ポップ処理(手順8の処理)にコンテキストの戻りアドレスに分岐(ハードウェア処理)
EXC_RETURNコード
割り込み、例外復帰時に戻すコード。LRに書かれているこのコードがPCに書かれ実行されると、自動的にスタックからポップ処理が行われ、戻りアドレスへと分岐する(ハードウェアの処理)
テールチェイン
- 2つの割り込みが同時起こった際は、まず、優先度の高い割り込みの処理が行われ、その次に優先度の低い方の割り込みの処理が行われる。このとき、優先度の高い割り込みが終了した際、割り込みの発生箇所へ戻らず、続けて優先度の低い方の割り込みが行われる
横取り
- 割り込み処理中に、その割り込みよりも優先度の高い割り込みが入る場合、新しいコンテキストをプッシュし、優先度の高い割り込みが行われる
- 優先度高→優先度低ならばテールチェイン、優先度低→優先度高ならば横取り
MPU
- memory protection unit
- RTOS上のスタックのオーバーフローの検知
- flash領域、ROM領域などへライトを行うのを禁止する
- データ領域を実行できないようにする
- 特権モード以外の場合に特定領域へアクセスできないようにする
- などを可能にする。
systick
-
OS専用タイマ。24bitのダウンカウンタで、0になるとsystick例外を起こす。
-
カウント値はSYST_CVRからread可能。0になった時はSYST_RVRの値がカウンタに設定される
-
優先順位もSHPR3(bit 31~24)で設定可能
SVC(スーパバイザコール)
-
SVC命令を行うと、スーパーバイザコール割り込みが発生。
-
cortex-Mではスーパーバイザコール専用の例外ベクタを持っている。
pendSV
-
ICSRレジスタのbit28に1をライトすることでpendSV状態にできる
-
pendSV状態でスーパーバイザコール割り込みが来た際、割り込み状態で、pendSV割り込みよりも優先度が高い割り込み・例外がなくなった時点でpendSV割り込みが発生する
-
優先順位もSHPR3(bit 23~16)で設定可能
-
pendSVとSVC割り込みに関しては同時に起きないようにしなければならない。
systickタイマ
-
定周期の割り込みや、実行時の時間管理のための24bitタイマ。RTOSでも時間管理でこれを使う
-
以下のレジスタがある
- SYST_CSR コントロールステータスレジスタ
- SYST_RVR リロード値レジスタ
- SYST_CVR カレント値レジスタ
- SYST_CALIB キャリブレーション値レジスタ
-
動作としては、
- SYST_CSRのENABLE bit を1にすることでカウントスタート。カウント値はSYST_CVRの値がデクリメントされていく
- SYST_CVRの値が0になると、SYST_RVRレジスタ値がSYST_CVRに書き込まれ、再度デクリメントをスタートさせる
その他
スタートアップルーチン
- C言語の規定?により、初期値の設定がない変数はBSS領域に配置し、初期値を0にしておく
- 初期値のある変数はDATA領域で定義しておく
- 初期値の有無にかかわらず、32bitマイコンではROM領域に初期値を配置しておき、スタートアップルーチン中にROM領域からRAM領域へコピーして使われることが多い
参考文献
-
APSさんのcortex-M解説記事全般(わかりやすいのでおすすめです)
https://www.aps-web.jp/category/academy/cm/ -
armマイコンについて
https://www.wdic.org/w/SCI/ARM%E3%82%A2%E3%83%BC%E3%82%AD%E3%83%86%E3%82%AF%E3%83%81%E3%83%A3 -
アセンブラの学習関連
https://github.com/karino2/c-lesson/blob/master/arm_asm.md
https://jhalfmoon.com/dbc/2019/09/09/%E3%81%90%E3%81%A0%E3%81%90%E3%81%A0%E4%BD%8E%E3%83%AC%E3%83%99%E3%83%AB%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B04-arm-thumb%E5%88%87%E3%82%8A%E6%9B%BF%E3%81%88/