アンアラインド・データ・アクセス、動作モード、特権アクセス

アンアラインド・データ・アクセス

Cortex-M3はアンアラインド・データ・アクセスをサポートしています。8ビット、16ビット、32ビットデータをRAM等のメモリに格納する場合、アンアラインド・データ・アクセスをサポートしていれば、無駄な領域を作らず各々のデータをRAMに格納することができます。例えばSRAMメモリでは25%以上削減可能です。

アンアラインド・データ・アクセス

Cortex-M3は32ビットマイコンですから、32ビット毎にアライメントされています。すなわちCPUがアクセスするデータの単位は32ビットです。しかし8ビットや16ビットデータをメモリに格納する際、アンアラインド・データ・アクセスを使わなければ、32ビットのアラインメント(境界線)をまたいでデータを格納することはしません。そのため、8ビットデータなら32-8=24ビット、16ビットデータなら32-16=16ビットの無駄な領域ができることになります。(下図左側参照)。アンアラインド・データ・アクセスを使うと、 32ビットのアラインメント(境界線)をまたいで、順番にデータを格納します。(下図右側参照)このため、メモリを無駄なく有効に使うことができます。

図1

記述例(IARのツールの場合)

アラインメントをまたぐデータをアクセスするためには、#pragma pack(n) を付加した構造体を定義します。下記が記述例です

								#pragma pack(1)
								struct {
									short s;	// 2 byte
									char c;		// 1 byte
									long l;		// 4 byte
									char c2;	// 1 byte
								} s;
								

これが8バイトにおさまります。

コンパイラの種類やコンパイラのバージョンによって割り当ての仕方が異なるものなのでしょうか?データの割り当ては通常は同じです。ただし、常に同じであることを保証している訳ではありませんので気をつけて下さい。(変わっていても文句は言えません)

プログラムの記述方法については、どうでしょう?データの配置の仕方は各コンパイラのマニュアルで定義されていますので、マニュアルに従って下さい。(例えば、ARM IAR C/C++ Development Guide

アンアラインド・データ・アクセスはCortex-M3で初めて取り入れられた手法ではありません。導入されたのはARMv6です。

使用上の注意点

アンアラインド・データ・アクセスは便利なように見えますが、注意点が一つあります。Cortex-M3は32ビットマイコンですから、32ビットのアラインメント(境界線)をまたいだデータをアクセスするときは、CPUは2回に分けてアクセスすることになります。これはCPUの効率を落とすことを意味します。

アンアラインド・データ・アクセスを使用すると、RAMを節約できる代わりにCPUの実行時間が犠牲となることを認識しておいてください。ですから、アラインメントをまたぐデータのアクセスは、構造体を経由した特殊なアクセスと考えたほうが自然です。アラインメントでロスが出るのは型(長さ)の違うデータが宣言されているところの隣り合う部分だけですし、プログラム側でメモリの長さや種類が混ざって宣言されていても、一箇所に集められますので、フィールド定義が予め決められている場合以外で使用することはあまり多くないと思います。

また、Cortex-M0では、採用されていませんので、Cortex-M3からCortex-M0に移行する際は注意が必要です。

特権、モード、スタック

Cortex-M3には動作する状態に関係する重要な要素が3つあります。それは、特権とモードとスタック(スタックポインタ)です。これらは、すべて関連性を持っており、各々の要素が関連付けられながらCortex-M3は動作します。

特権

特権とは、CPUに与えられる権利です。CPUの持つ色々なリソースへのアクセス権を意味するものです。特権を与えられるとCPUは、すべてのリソースへアクセスできるようになります。しかし非特権の場合には、一部のリソースへのアクセスが制限されるか排除されます。

モード

モードとは、CPUが動作する状態を指します。ハードウェア(ハンドラ)によって動作するモードとユーザのソフトウェアによって動作するモードがあります。ハードウェア(ハンドラ)によって動作するモードをハンドラモードと呼び、ユーザのソフトウェアによって動作するモードをスレッドモードと呼びます。ハンドラモードではCPUは、必ず前述した特権を持って動作します。

スタック

スタックは割り込み等が発生したときにCPUの状態(コンテキスト)を保存しておく場所です。通常のマイコンには一つしかありませんが、Cortex-M3には2種類あります。この2つのスタックの使われ方は、前述の特権とモードに関わっています。

これらの要素は、少し複雑ですが、理解すると大変便利なものです。これから詳細に説明いたします。

動作モード

Cortex-M3は、特権アクセスと非特権アクセスの他に、スレッドモードとハンドラモードの2つの動作モードをサポートしています。リセット後等の通常時はスレッドモードですが、例外処理等が発生したときにハンドラモードに入ります。ここで言うハンドラとは、割り込み、フォールト等の例外処理によって起動される専用の内部プログラム(ハードの動作も含む)のことを言います。言い換えると、(若干語幣があるかもしれませんが)スレッドモードは通常処理を行うモードで、ハンドラモードは例外処理を行うモードだと言ってもいいでしょう。

スレッドモード

プロセッサは、リセット後はスレッドモードに入ります。この時、特権アクセスの状態です。その後(前述したように)ユーザーはMSR命令を使用してCONTROL[0]ビットをクリアすることにより、非特権アクセスに移ることができます。 すなわち、スレッドモードの場合は、ユーザーが特権アクセスか非特権アクセスかを選択することができます。

リセット後、Cortex-M3はmain.cに記述されたプログラムコードを実行します。そして、割り込みやフォールトの例外処理が発生するとハンドラモードに移ります。しかし、割り込みやフォールトのサービスルーチンからmain.cのコードに戻ってくると、再びスレッドモードに戻ります。このことから、スレッドモードとは、簡単に言うとmain.cに記述されているプログラムコード(リンクされているサブルーチンも含む)を実行するモードだと言えます。

繰り返しますが、スレッドモードで、特権アクセスから非特権アクセスに変更されると、ユーザでは特権に戻すことはできません。例外処理が発生する等の動作で、ハンドラがスレッドモードを特権に変更します。言い換えると、ハンドラモードは常に特権です。

ハンドラモード

ハンドラモードには、割り込みやフォールトの例外処理の結果として入ります。割り込みサービスルーチンやフォールトのサービスルーチンは必ずハンドラモードによって実行され、かつ特権アクセスで実行されることになります。下図は特権アクセスと非特権アクセス、スレッドモードとハンドラモードの状態遷移を示しています。リセット後は必ず、特権アクセスのスレッドモードでプログラムは開始されます。もしユーザーが非特権アクセスに移った場合は、図の下方にある非特権アクセスのスレッドモードに移ることになります。

特権アクセスと非特権アクセスのどちらのモードのスレッドモードに居ても、例外処理が発生すると、必ず図中真ん中にあるハンドラモードに移ります。ハンドラモードは必ず特権アクセスです。もし、ハンドラモードの中でさらなる例外処理が発生すると、ネスティングされます。例処理のサービスルーチンが終了すると、再び元に居たスレッドモードに戻ります。

図2

特権アクセスおよび非特権アクセス

特権アクセスおよび非特権アクセス

特権アクセスと非特権アクセスはスーパーバイザアクセスとユーザーアクセスとも呼ばれます。特権アクセスが使える状態を特権モード、非特権アクセスの場合のモードを非特権モードと呼んだりします。モトローラ(現NXP)のMC6800系のマイコンを使ったことのある方は、親しみやすいと思いますが、初めての方には理解し難いかも知れません。Cortex-M3の特権アクセスと非特権アクセスはARM7の特権アクセスと非特権アクセスと同じです。

8ビットマイコンや16ビットマイコンではあまり見られませんが、32ビットマイコンになるとOSを使う用途が増えますので、特権アクセスと非特権アクセスが非常に便利になります。

特権アクセスと非特権アクセスで、何が違うのかと言うと、特権アクセスは何でもできるのですが、非特権アクセスでは一部のリソースへのアクセスが制限されるか排除されます。具体的には非特権アクセスは次のことが防止されます。

  • ●FAULTMASKとPRIMASKをセットするCPSなどの一部の命令の使用
  • ●システム制御空間(SCS)(System Control Space)での大部分のレジスタへのアクセス

従って、マイコンを、システム管理者が使う部分とユーザが使う部分とに分けて使うシステムでは、効果的な機能です。

一般的にはOSのカーネル等は特権アクセスで実行され、その他のタスク/スレッド(ユーザープログラム)は非特権アクセスで実行したりして区分します。こうしておくと、非特権アクセスで実行しているプログラムがOSのリソースを誤って破壊することから守ることができます。また、ハッカー等によって故意に組み込まれたウィルスプログラム的なものからも、システム(OS等)を守ることができます。

リセット後は特権アクセスになりますが、MSR命令を使用してCONTROL[0]ビットをクリアすることにより、非特権アクセスに変更することができます。特権アクセスから非特権アクセスに変更されると、ユーザでは特権アクセスに戻すことはできません。例外処理等が発生し、ハンドラ(ハードウェア)のみが特権アクセスに変更できます。

変な言い方になるかもしれませんが、特権アクセスができれば何でもできますので、あえて非特権アクセスを使わなくてもいいわけです。最初から最後まで特権(スーパーバイザ)モードで使っても良いわけです。Cortex-M0ではコストとエネルギー効率が優先になっているので、この概念がありません。すべてが、特権アクセスということになります。

しかし、OS等を使って、システムプログラムとユーザープログラムを実行させる場合には、セキュリティの観点から特権アクセスと非特権アクセスを利用されることをお勧めいたします。