この節ではAVRコア・アーキテクチャの一般的な内容について説明します。CPUコアの主機能はプログラムの正しい実行を確実に行うことです。 したがってCPUには、メモリへのアクセス、計算の実行、周辺回路の制御、そして、割り込み処理を行う能力が必要です。
処理速度と並行処理の効率を最大化するため、AVRはハーバード・アーキテクチャ(プログラムとデータそれぞれに分離されたメモリとバスを使用する構造)を採用しています。プログラム・メモリの命令は単一のパイプラインによって実行されます。一つの命令が実行されている間に、次の命令がプログラム・メモリから先読みされます。この設計により、すべての命令が1クロックで実行されます。プログラム・メモリは In-System Programming(ISP)により再プログラム可能なフラッシュ・メモリです。
高速アクセス可能なレジスタ・ファイルには32個x8ビットの1クロック・アクセス可能な汎用レジスタがあり、それぞれのレジスタにはです。これにより、算術論理ユニット(ALU)が1クロックで処理を行うことが可能になります。典型的なALUの処理では、二つのオペランドがレジスタ・ファイルから出力され、演算が実行され、そして実行結果をレジスタ・ファイルに書き戻す、この一連の処理を1クロックで実行します。
32個のレジスタのうち6個については、3つの16ビット間接アドレス・ポインタ・レジスタとして、データ領域のアドレス参照に使用することができ、効率的なアドレス計算が可能です。このうち一つのアドレス・ポインタは、プログラム・フラッシュメモリ上のルックアップ・テーブルのアドレス・ポインタとしても使用できます。これらの追加機能をもつレジスタは、16ビットX、Y、Zレジスタと呼ばれ、この節で後ほど詳しく説明します。
ALUは、レジスタとレジスタ、レジスタと定数の算術演算および論理演算をサポートします。単一レジスタに対する演算処理もALU内で実行されます。算術処理の直後にステータス・レジスタが更新され、計算結果についての情報を反映します。
プログラム・フローの制御機能は、全アドレス空間を直接指定する、条件、または無条件のジャンプ命令やコール命令により提供されます。ほぼすべてのAVR命令は単一の16ビットワードの形式になっており、プログラムメモリの各アドレスには、16ビットまたは32ビットの命令コードが含まれます。
プログラム・フラッシュメモリ空間は二つの領域、ブートプログラム領域とアプリケーション・プログラム領域、に分割されています。どちらの領域にも個別に、書き込み保護および読み書き保護のLockビットがあります。アプリケーション・フラッシュメモリ領域に書き込みを行うSPM命令は、ブートプログラム領域上で実行する必要があります。
割り込み、およびサブルーチン・コールの間、戻りアドレスを示すプログラム・カウンタ(PC)の値がスタック上に保持されます。スタックは実用性のため、汎用データSRAM領域上に割り当てられており、したがってスタックのサイズはSRAMのサイズと、その使用状況によってのみ制限されます。すべてのユーザー・プログラムでは、リセット処理中で(サブルーチンまたは割り込み処理が実行される前に)スタック・ポインタ(SP)を初期化する必要があります。スタック・ポインタ(SP)は、I/O空間上で読み書きアクセスが可能です。データSRAM領域は、AVRアーキテクチャでサポートされている、5つの異なるアドレッシング・モードで簡単にアクセス可能です。 AVRアーキテクチャのメモリ空間は、すべてリニアで標準的なメモリマップとなっています。
柔軟性の高い割り込みモジュールには、I/Oアドレス空間上にある各割込みのコントロール・レジスタに加え、ステータス・レジスタ内にグローバル割込み許可ビットがあります。すべての割込みには、割込みベクトル・テーブル内に別々の割込みベクトルがあります。割込みは、それぞれの割込みベクトルの順序にしたがって優先順位が決まります。割込みベクトルのアドレスが低い位置にあるほど優先度が高くなります。
I/Oメモリ空間には64のアドレスがあり、コントロール・レジスタ、SPI、そして入出力ポートという形でCPU周辺機能が含まれています。I/Oメモリは直接アクセスすることも、レジスタ・ファイルに続く0x20 - 0x5Fのデータ空間アドレスからもアクセスできます。さらに、ATmega48/88/168 には、拡張I/O領域がSRAM上の0x60 - 0xFFにあり、そこではST/STS/STD と LD/LDS/LDD 命令のみが使用可能です。
高性能のAVR ALUは、32個すべての汎用レジスタと直接接続された状態で演算動作を行います。1クロック以内で、汎用レジスタ間またはレジスタと即値定数との間で算術演算が実行されます。ALUの演算動作は三つのカテゴリーに分類されます ー 算術演算、論理演算、そしてビット操作です。AVRアーキテクチャの実装のなかには、符号あり/符号なし乗算、および小数フォーマットをサポートする強力な乗算器を提供するものもあります。
詳細な説明については、「命令セット“Instruction Set”」の節を参照してください。
ステータス・レジスタには、最後に実行された算術命令の結果についての情報が格納されています。この情報を用いて、条件付き処理を行うためにプログラムの実行フローを切り換えることができます。 ステータス・レジスタは、命令セットのリファレンス中で示されているように、すべてのALU演算動作の結果によって更新されることにご注意ください。これにより多くの場合、専用の比較命令を用いる必要がなくなり、結果的により高速でサイズの小さいコードを生成することができます。
割込み処理が始まる際のステータス・レジスタの値の退避、および、割込みから戻る際の値の復帰は、自動的には行われません。この操作はソフトウェアにより行う必要があります。
AVR ステータス・レジスタ – SREG – の定義は下記のとおりです:
ビット | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
0x3F(0x5F) | I | T | H | S | V | N | Z | C | SREG |
Read/Write | R/W | R/W | R/W | R/W | R/W | R/W | R/W | R/W | |
初期値 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
グローバル割込み許可ビットは割込みを有効にする場合に、セットする必要があります。そのうえで、個々の割込み許可の制御を、それぞれのコントロール・レジスタで行います。グローバル割込み許可ビットがクリアされている場合、個々の割込み許可設定に関係なく、すべての割込みが無効となります。Iビットは、ハードウェアにより割込みが発生した直後にクリアされ、RETI命令によって以降の割込みを許可するためにセットされます。 命令セット・リファレンスにあるように、アプリケーション・ソフトウェアによりSEI、CLI命令を使用して、Iビットをセット、クリアすることもできます。
ビット・コピー命令のBLD(ビット・ロード)とBST(ビット・ストア)は、Tビットをコピー元またはコピー先対象ビットとして使用します。レジスタ・ファイルのレジスタ内の1ビットをBST命令によりTビットにコピーし、また、Tビットの値をBLD命令によりレジスタ・ファイルのレジスタ内の1ビットにコピーすることができます。
ハーフキャリー・フラグHは、一部の算術演算において発生するハーフキャリーを示します。ハーフキャリーはBCD演算において用いられます。詳細については、「命令セット」を参照してください。
符号フラグSは、常に負号フラグNと2の補数オーバーフロー・フラグVの排他的論理和となります。詳細については、「命令セット」を参照してください。
オーバーフロー・フラグVは、2の補数による演算をサポートします。詳細については、「命令セット」を参照してください。
負号フラグNは、算術演算あるいは論理演算の結果が負数となったことを示します。詳細については、「命令セット」を参照してください。
ゼロ・フラグは、算術演算あるいは論理演算の結果がゼロとなったことを示します。詳細については、「命令セット」を参照してください。
キャリー・フラグは、算術演算あるいは論理演算の結果により発生したキャリーを示します。詳細については、「命令セット」を参照してください。
レジスタ・ファイルは、AVR高機能RISC命令セット用に最適化設計されています。要求される実効速度と柔軟性を実現するため、レジスタ・ファイルでは次のような入力/出力手段がサポートされています:
Figure 6-2に、CPU内の32個の汎用レジスタの構造を示します。
7 | 0 | アドレス | ||
R0 | 0x00 | |||
R1 | 0x01 | |||
R2 | 0x02 | |||
... | ||||
R13 | 0x0D | |||
R14 | 0x0E | |||
R15 | 0x0F | |||
R16 | 0x10 | |||
R17 | 0x11 | |||
... | ||||
R26 | 0x1A | Xレジスタ 下位バイト | ||
R27 | 0x1B | Xレジスタ 上位バイト | ||
R28 | 0x1C | Yレジスタ 下位バイト | ||
R29 | 0x1D | Yレジスタ 上位バイト | ||
R30 | 0x1E | Zレジスタ 下位バイト | ||
R31 | 0x1F | Zレジスタ 上位バイト |
レジスタ・ファイルを操作する命令のほとんどが、すべてのレジスタに直接アクセスでき、それらのほぼすべての命令が1クロック命令となっています。
Figure 6-2にあるとおり、各々のレジスタはデータメモリとしてのアドレスも割り当てられており、ユーザー・データメモリ空間の最初の32アドレスに直接マッピングされています。実際の回路がSRAMとして実装されているわけではありませんが、このメモリ構造により、X、Y、Zポインタ・レジスタによりあらゆるレジスタを指すように設定でき、汎用レジスタへのアクセスに非常に高い柔軟性を提供しています。
レジスタR26..R31 には、汎用レジスタとしてのほかに追加機能があります。これらのレジスタは、データメモリ空間への間接アドレッシングのための16ビットのアドレス・ポインタとなります。3つの間接アドレス・レジスタ、X、Y、Zの内容はFigure 6-3のように定義されています。
15 | XH | XL | 0 | |||
Xレジスタ | 7 | 0 | 7 | 0 | ||
R27 (0x1B) | R26 (0x1A) | |||||
15 | YH | YL | 0 | |||
Yレジスタ | 7 | 0 | 7 | 0 | ||
R29 (0x1D) | R28 (0x1C) | |||||
15 | ZH | ZL | 0 | |||
Zレジスタ | 7 | 0 | 7 | 0 | ||
R31 (0x1F) | R30 (0x1E) |
これらのアドレス・レジスタは異なるアドレッシング・モードとして、固定ディスプレースメント、自動インクリメント、自動デクリメントとしての機能を持っています。(詳細については、命令セット・リファレンスを参照してください)
スタック領域は主に一時的なデータを保存したり、ローカル変数の格納場所としたり、割込みやサブルーチン処理の後に戻るアドレスを保存したりするのに使います。スタック・ポインタ・レジスタは常にスタック領域の先頭アドレスを指しています。スタック領域は、メモリアドレスの高位から低位へと、サイズが増大するように実装されていることにご注意ください。スタックへのPUSH命令により、スタック・ポインタの値が減少する、ということです。
スタック・ポインタは、サブルーチンと割込み用のスタックとして使われるデータSRAMのスタック領域を指しています。データSRAMのスタック領域は、あらゆるサブルーチン・コールの実行、および割込みが許可されるより以前に定義されていなければなりません。スタック・ポインタは、0x0100よりも高いアドレス、できればRAMENDに初期設定してください。
スタック・ポインタは、データをスタックにPUSH命令により格納する際に1減少、POP命令によって復帰する際に1増加し、サブルーチンから戻るためのRET命令や、割込みから戻るためのRETI命令によりデータが復帰する際に2増加します。
AVRのスタック・ポインタは、I/Oアドレス空間上の2個の8ビットレジスタとして実装されています。実際に使用されるビット数は、各デバイスの実装に依存します。AVRアーキテクチャのデバイスの一部には、データスペースが小さいため、必要なレジスタがSPLのみとなる場合もあることに注意してください。この場合、SPHレジスタは実装されていません。
ビット | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
0x3E(0x5E) | SP15 | SP14 | SP13 | SP12 | SP11 | SP10 | SP9 | SP8 | SPH |
0x3D(0x5D) | SP7 | SP6 | SP5 | SP4 | SP3 | SP2 | SP1 | SP0 | SPL |
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | ||
Read/Write | R/W | R/W | R/W | R/W | R/W | R/W | R/W | R/W | |
R/W | R/W | R/W | R/W | R/W | R/W | R/W | R/W | ||
初期値 | RAMEND | RAMEND | RAMEND | RAMEND | RAMEND | RAMEND | RAMEND | RAMEND | |
RAMEND | RAMEND | RAMEND | RAMEND | RAMEND | RAMEND | RAMEND | RAMEND |
この節では、命令実行時の一般的なアクセスタイミングについて述べます。AVR CPUは、CPUクロックclkCPU によって動作しており、そのクロックはチップのクロック源として選択されたものから直接生成されています。内部でクロックの分割は行っていません。
Figure 6-4 に、命令フェッチと命令実行の並行動作がハーバード・アーキテクチャとレジスタ・ファイルへの高速アクセスによって実現されている様子を示します。これは、1MHzあたり最大1MIPSの性能を得るための基本的なパイプライン設計思想であり、これにより、コストあたりの速度、クロック周波数あたりの速度、そして単位消費電力あたりの速度、それぞれについて最良の結果を実現します。
Figure 6-5 に、レジスタ・ファイルの内部タイミングの概念を示します。1クロック中で、2個のオペランドに対してALU演算動作が行われ、格納先レジスタに演算結果が書き戻されます。
AVRでは、多種多様の割込み要因を提供します。これらの割込み、およびリセット・ベクトルには、プログラム・メモリ空間に個別のプログラム・ベクトルがあります。すべての割込みには個別の許可ビットが割り当てられており、割込みを有効にするには各々のビットに論理値1を書き込んだ上で、ステータス・レジスタのグローバル割込み許可ビットをセットする必要があります。プログラム・カウンタの値によっては、BLB02あるいはBLB12ロック・ビットが設定されていると、割込みが自動的に無効になる場合があります。この機能により、ソフトウェアのセキュリティを高めています。詳細については、「メモリ・プログラミング」(p.284)を参照してください。
プログラム・メモリ空間の最小アドレス位置には、初期状態では、リセット・ベクトルおよび割込みベクトルとして定義されています。ベクトルの全リストは「割込み」(p.55)にあります。このベクトルのリストは同時に各割込みの優先順位を決定しています。アドレスが低い位置にあるほど、優先レベルが高くなります。 RESETは最も高い優先度を持ち、つづいてINT0(外部割込み要求0)が高くなります。割込みベクトル領域は、MCUコントロール・レジスタ(MCUR)のIVSELビットの設定により、ブート・フラッシュ領域の最初の位置に移動することができます。より詳しい内容については、「割込み」(p.55)を参照してください。リセット・ベクトルは、BOOTRSTヒューズ・ビットを設定することによっても、ブート・フラッシュ領域の最初の位置に移動することができます。「ブート・ローダーのサポート – 読み書き並行自己プログラミング ATmega88/ATmega168」(p.268)を参照してください。
割込みが発生すると、グローバル割込み許可ビットIがクリアされ、すべての割込みが禁止されます。ユーザー・ソフトウェアによって、論理値1をIビットに書き込むことで、割込みのネスティングを有効にでき、すべての有効な割込みを、現在処理している割込みルーチンを中断して実行させることができます。Iビットは、割込み復帰命令RETIが実行されると、自動的にセットされます。
割込みには基本的に二種類のものがあります。 ひとつは割込みフラグをセットするイベントによってトリガーされるものです。このタイプの割込みでは、プログラム・カウンタを割込み処理ルーチンの実行のため、割込みベクトルのアドレスへと変更し、ハードウェアが該当する割込みフラグをクリアします。 割込みフラグは、論理値1を該当ビットに書き込むことによってもクリアできます。それぞれの割込みの許可ビットがクリアされている状態で、割込みの条件が満たされた場合、割込みフラグはセットされ、次に割込みが有効になるか、ソフトウェアによってクリアされるまで記憶されます。同様に、グローバル割込み許可ビットがクリアされている間に、一つ以上の割込み条件が満たされた場合、該当する割込みフラグがセットされ、グローバル割込み許可フラグがセットされるまで記憶され、優先度の順位にしたがって実行されます。
二つめのタイプは、割込み条件が満たされている間、トリガーされるものです。これらの割込みには、必ずしも割込みフラグが備わっているとは限りません。割込みが有効になる前に割込み条件が解消された場合、割込み動作は起動されません。
AVRが割込み処理を終えるときは、待機中になっている割込みを処理する前に、必ずメイン・プログラムに戻り、一つ以上の命令が実行されます。
ステータス・レジスタは、割込み処理開始時に自動的に退避されたり、割込み終了時に復帰されたりしない点に注意してください。これはソフトウェアで行う必要があります。
CLI命令を使って割込みを禁止すると、割込みは即座に無効状態となります。CLI命令の実行後、たとえCLI命令と同時に発生したものであっても、いかなる割込みも実行されません。下記の例では、EEPROM書き込みシーケンスにおいて割込みを回避するための利用例を示しています。
アセンブリ言語での例 |
|
C言語での例 |
|
SEI命令を使って割込みを許可すると、待機中になっている割込みを処理する前に、下記の例のとおり、SEI命令直後の命令が必ず実行されます。
アセンブリ言語での例 |
|
C言語での例 |
|
有効になっているAVRの割込みはすべて、割込み処理実行までの時間が最短で4サイクル必要です。4クロック経過後、実際の割込み処理ルーチンのためのプログラム・ベクトル・アドレスの実行を開始します。この4クロック期間で、プログラム・カウンタをスタックに退避します。割込みベクトルは通常割込み処理ルーチンへのジャンプになっており、このジャンプに3クロックを要します。もし、割込みが複数サイクルを必要とする命令の実行中に発生した場合は、その命令の実行が完了した後、割込み処理が開始します。MCUがスリープ・モードになっているときに割込みが発生した場合、割込み処理開始までの時間は、さらに4クロック増加します。この増加分はスリープ・モードからのスタートアップ時間によるものです。
割込み処理ルーチンからの復帰には、4クロック必要です。この4クロック期間で、プログラム・カウンタ(2バイト)をスタックから復帰し、スタック・ポインタを2増加し、そしてSREGのIビットをセットします。