ライトバッファとバリア命令

ライトバッファ

ライトバッファとは

一般的に、プロセッサコアクロックに対してバスクロックは低速で、プロセッサコアがメモリに書き込みが完了するまで待機すると、実行速度が低下します。この問題を解消するため、プロセッサコアとバスインターフェースユニット間にライトバッファ(FIFOメモリ)を設け、プロセッサコアの実行速度低下を抑えます。プロセッサコアは、コアクロック速度でライトバッファに書き込めるので、バスインターフェースユニットへの書き込み待機は発生しません。ライトバッファに空きがない場合は、空きができるまでプロセッサコアは待機状態になります。

ライトバッファを制御するDMB(データメモリバリア)命令DSB(データ同期バリア)命令、パイプラインをフラッシュ(破棄)するISB(命令同期バリア)命令の3種類をバリア命令と言います。

図1

ライトバッファの段数は、テクニカルリファレンスマニュアルを参照ください。尚、Cortex-A9の場合はデータ結合機能を持つ4つの64ビットスロットです。

ライトバッファ制御の必要性

ARMプロセッサはメモリタイプとアクセスオーダ(第10回参照)に従ってメモリアクセスを行います。デバイスメモリに配置されているペリフェラルとノーマルメモリに配置されているメモリ間でアクセス順序を守る場合、DMB命令またはDSB命令によるライトバッファの制御が必要です。さらに、キャッシュの制御(Clean(クリーン)/Invalidate(無効))を合わせた検討が必要です。

【メモリタイプとキャッシュ・ライトバッファの関連性一覧】
No メモリタイプ キャッシュ ライトバッファ
1 ノーマルメモリ
2 デバイスメモリ ×
3 ストロングリオーダメモリ × ×

バリア命令

バリア命令の動作

アセンブリ言語でのバリア命令

動作制限は非推奨のため設定を省略します。

No 命令名称 アセンブリ命令
1 データ同期バリア DSB
2 データメモリバリア DMB
3 命令同期バリア ISB

C/C++言語でのバリア命令

ARMコンパイラは、「ARM C Language Extensions 2.0」で、C/C++言語から使用できる、3種類のバリア命令が定義されています。accs(引数)の動作制限は非推奨のため、デフォルト値の15を設定します。

No 命令名称 関数名称
1 データ同期バリア void __dsb(unsigned int accs)
2 データメモリバリア void __dmb(unsigned int accs)
3 命令同期バリア void __isb(unsigned int accs)

DMB命令の動作

DMB命令以前に存在するすべてのメモリアクセスが、先にアクセス完了することを保障します。メモリアクセス命令以外の「ADD r0,r1,r2」命令は実行します。

図2

【プログラム例】

DSB命令の動作

DSB命令以前に存在するすべてのメモリアクセスが完了するまで、以後の命令を実行しないように、プロセッサを待機状態にします。メモリアクセス命令と「ADD r0,r1,r2」命令も実行しません。

図3

【プログラム例】

ISB命令の動作

ARMプロセッサのパイプラインがフラッシュ(破棄)されることを保障します。パイプラインステージの命令が破棄され、命令はメモリシステムから再度フェッチされます。

スタンバイモードとバリア命令を使用した場合の事例

ペリフェラルからの割り込みにより、スタンバイモードから復帰し処理を継続する場合について解説します。ペリフェラルアクセス後に、WFI(*1)組み込み関数を使用しプロセッサをスタンバイモードにする場合、DSB組み込み関数を使用します。

動作説明

STATUSレジスタは0x40000000番地にメモリマップされたレジスタで、0x00000003を書き込むことにより、入力クロックでCOUNTレジスタを減算し、0x0で割り込みが発生します。COUNTレジスタは0x40000004番地にメモリマップされたレジスタで、割り込みが発生するまでの時間を設定します。STATUS/COUNTレジスタは「デバイス」に配置しています。

プログラム解説

  • ①割り込みを発生するために、STATUS/COUNTレジスタに設定値を書き込みます。
  • ②STATUS/COUNTレジスタの書き込みは、ライトバッファに書き込みが終了した時点で次の命令を実行します。
  • ③WFI組み込み関数の実行で、ARMプロセッサコアがスタンバイモードに入るので、ライトバッファの内容がSTATUS/COUNTレジスタに書き込まれている保障はありません。
  • ④「ノーマルメモリ」と「デバイスメモリ」のアクセス順序は保障されませんので、DSB組み込み関数で書き込み完了まで待機します。

【プログラム例】

								#define STATUS (*(volatile unsigned long *)0x40000000) // ステータスレジスタ
								#define COUNT  (*(volatile unsigned long *)0x40000004) // カウンタレジスタ

								    COUNT   = 10000;                      // 一定時間プロセッサの動作を停止
								    STATUS = 0x00000003;                  // 動作と割り込み許可
								    __wfi();                              // プロセッサコアをスタンバイへ
								

【修正プログラム例】

								#define STATUS (*(volatile unsigned long *)0x1C010000) // ステータスレジスタ
								#define COUNT  (*(volatile unsigned long *)0x1C010004) // カウンタレジスタ

								    COUNT   = 10000;                      // 一定時間プロセッサの動作を停止
								    STATUS = 0x00000003;                  // 動作と割り込み許可
								    __dsb(15);                            // すべてのメモリアクセスを完了
								    __wfi();                              // プロセッサコアをスタンバイへ