PMUの概要
プログラム性能の評価で実行時間を測定する場合、デバッグツールのトレース(プログラムの実行履歴)機能で、実行時間を測定することが今まで一般的でしたが、プロセッサの高速化に伴い、Armプロセッサからトレース情報を出力することができない場合が見受けられます。この課題を解決するために、Cortex-Aシリーズでは、サイクル数を測定するサイクルカウンタと特定の事象を測定するイベントカウンタで構成されたPMUを搭載しています。
プログラム実行は、PMUの有効・無効に影響(*1)を受けることがなく、実行サイクル数やいろいろな事象を測定する機能です。PMUのサイクルカウンタおよびイベントカウンタは共に32ビットカウンタで、サイクルカウンタの測定点数は1点、イベントカウンタの測定点数はプロセッサコアにより異なり、Cortex-A9は6点搭載しています。サイクルカウンタおよびイベントカウンタのオーバーフローに関しては、オーバーフロー割り込みを利用することで、長時間の測定が可能です。
(*1)サイクルカウンタおよびイベントカウンタのオーバーフロー割り込みを使用した場合、割り込みによりプログラム実行に影響を与えます。
PMUの使用方法
PMUのサイクルカウンタを使用し、関数の実行時間測定方法を解説します。
- PMUに関連するレジスタは、ユーザモードでのアクセスは禁止されていますので、PMUSERENR(ユーザイネーブルレジスタ)を特権モードでユーザモードアクセス許可を設定します。PMUSERENRについては、 後述の該当項目を参照ください。
- PMUはリセット時に非稼働状態となっているので、稼働設定を行います。
- 測定時間は、動作クロックが1GHzの場合、約4.2秒となりますので、必要に応じてクロックを1/64分周に設定またはオーバーフロー割り込みを使用することを検討します。
- サイクルカウンタ値を保存する変数にvolatile修飾子を付加します。
- サイクルカウンタの許可・リセットを行い、開始サイクル数を変数に保存します。
- 測定対象の処理を実行します。
- 終了サイクル数を変数に保存します。
- デバッグツール等を利用して、開始サイクル変数と終了サイクル変数から実行サイクル数を求めます。
【サイクル数測定サンプルプログラム】
volatile unsigned long start_pmu_counter; // 開始サイクル volatile unsigned long end_pmu_counter; // 終了サイクル … pmu_start(); // PMUを稼働状態 pmu_enable_counter(); // サイクルカウンタを許可 pmu_reset_counter(); // サイクルカウンタをリセット start_pmu_counter = pmu_read_counter(); // 開始サイクル数を取得 func1(); // 測定を実施する処理を実行 end_pmu_counter = pmu_read_counter(); // 終了サイクル数を取得 …
PMU制御関数
PMUで測定を行う場合の制御関数は、次の通りです。
No | 処理内容 | 関数名 |
---|---|---|
1 | PMUを稼働状態に設定します。 | void pmu_start(void) |
2 | サイクルカウンタをリセットします。 | void pmu_reset_counter(void) |
3 | イベントカウンタをリセットします。 | void pmu_reset_evnt_counter(void) |
4 | サイクルカウンタを稼働状態に設定します。 | void pmu_enable_counter(void) |
5 | イベントカウンタ0を稼働状態に設定します。 | void pmu_enable_event0(void) |
6 | イベントカウンタ0にイベントを設定します。 | void pmu_set_event0(int event) |
7 | サイクルカウンタ測定値を読み込みます。 | unsigned long pmu_read_counter(void) |
8 | イベントカウンタ0測定値を読み込みます。 | unsigned long pmu_read_event0(void) |
【PMU制御プログラム例】
; PMUを稼働状態に設定 ; void pmu_start(void) pmu_start MRC p15, 0, r0, c9, c12, 0 ; PMCR(パフォーマンスモニタ制御レジスタ)をリード ORR r0, r0, #0x1 ; Eビットをセット(稼働設定) MCR p15, 0, r0, c9, c12, 0 ; PMCRをライト BX lr ; サイクルカウンタをリセット ; void pmu_reset_counter(void) pmu_reset_counter MRC p15, 0, r0, c9, c12, 0 ; PMCRをリード ORR r0, r0, #0x4 ; Cビットをセット(サイクルカウンタをリセット) MCR p15, 0, r0, c9, c12, 0 ; PMCRをライト BX lr ; イベントカウンタをリセット ; void pmu_reset_evnt_counter(void) pmu_reset_evnt_counter MRC p15, 0, r0, c9, c12, 0 ; PMCRをリード ORR r0, r0, #0x2 ; Pビットをセット(イベントカウンタをリセット) MCR p15, 0, r0, c9, c12, 0 ; PMCRをライト BX lr ; サイクルカウンタを稼働状態に設定 ; void pmu_enable_counter(void) pmu_enable_counter MRC p15, 0, r0, c9, c12, 1 ; PMCNTENSET(カウントイネーブルセットレジスタ)をリード ORR r0, r0, #0x80000000 ; Cビットをセット(許可設定) MCR p15, 0, r0, c9, c12, 1 ; PMCNTENSETをライト BX lr ; イベントカウンタ0を稼働状態に設定 ; void pmu_enable_event0(void) pmu_enable_event0 MRC p15, 0, r0, c9, c12, 1 ; PMCNTENSETをリード ORR r0, r0, #0x1 ; P0ビットをセット(許可設定) MCR p15, 0, r0, c9, c12, 1 ; PMCNTENSETをライト BX lr ; イベントカウンタ0にイベントを設定 ; void pmu_set_event0(int event) pmu_set_event0 MOV r1, #0x0 ; イベントカウンタ0を選択 MCR p15, 0, r1, c9, c12, 5 ; PMSELR(イベントカウンタ選択レジスタ)にライト MCR p15, 0, r0, c9, c13, 1 ; PMXEVTYPER(イベント選択レジスタ)にライト BX lr ; サイクルカウンタ測定値を読み込み ; unsigned long pmu_read_counter(void) pmu_read_counter MRC p15, 0, r0, c9, c13, 0 ; PMCCNTR(サイクルカウントレジスタ)をリード BX lr ; イベントカウンタ0測定値を読み込み ; unsigned long pmu_read_event0(void) pmu_read_event0 MOV r0, #0x0 ; イベントカウンタ0を選択 MCR p15, 0, r0, c9, c12, 5 ; PMSELRにライト MRC p15, 0, r0, c9, c13, 2 ; PMXEVCNTRをリード BX lr
レジスタ一覧
Cortex-A9でPMUを使用する場合のレジスタについて説明します。詳細のレジスタの内容に関しては、「Armアーキテクチャリファレンスマニュアル Armv7-AおよびArmv7-Rエディション」を参照ください。
PMCR(パフォーマンスモニタ制御レジスタ)
このレジスタでは、PMUの稼働・非稼働、クロックカウンタおよびイベントカウンタのリセットを行います。
名前 | 機能 |
---|---|
N | 使用できるイベントカウンタの数です(Cortex-A9は6点実装されています)。 |
D | 測定クロックの分周比を設定します。 0:クロックを分周しません。 1:クロックを1/64分周を行います。 |
C | サイクルカウンタをリセットします。 0:動作を行いません。 1:サイクルカウンタをリセットします。 |
P | イベントカウンタをリセットします。 0:動作を行いません。 1:全イベントカウンタをリセットします。 |
E | PMUの動作を設定します。 0: PMUが非稼働状態です。 1: PMUが稼働状態です。 |
PMCNTENSET(カウントイネーブルセットレジスタ)
このレジスタでは、サイクルカウンタまたはイベントカウンタを稼働に設定します。
- 0:サイクルカウンタが非稼働です(書き込みは無効です)。
- 1:サイクルカウンタが非稼働にします。
名前 | 機能 |
---|---|
C | サイクルカウンタの稼働を設定します。 |
P5 | イベントカウンタ5の稼働を設定します。 |
P4 | イベントカウンタ4の稼働を設定します。 |
P3 | イベントカウンタ3の稼働を設定します。 |
P2 | イベントカウンタ2の稼働を設定します。 |
P1 | イベントカウンタ1の稼働を設定します。 |
P0 | イベントカウンタ0の稼働を設定します。 |
PMCNTENCLR(カウントイネーブルクリアレジスタ)
このレジスタでは、サイクルカウンタまたはイベントカウンタを非稼働に設定します。
- 0:サイクルカウンタが非稼働です(書き込みは無効です)。
- 1:サイクルカウンタが非稼働にします。
名前 | 機能 |
---|---|
C | サイクルカウンタの非稼働を設定します。 |
P5 | イベントカウンタ5の非稼働を設定します。 |
P4 | イベントカウンタ4の非稼働を設定します。 |
P3 | イベントカウンタ3の非稼働を設定します。 |
P2 | イベントカウンタ2の非稼働を設定します。 |
P1 | イベントカウンタ1の非稼働を設定します。 |
P0 | イベントカウンタ0の非稼働を設定します。 |
PMSELR(イベントカウンタ選択レジスタ)
このレジスタでは、使用するイベントカウンタを選択します。このレジスタに、取得するイベントカウンタ番号(Cortex-A9の場合は、0から5)を設定すると、PMXEVTYPER(イベントタイプ選択レジスタ)またはPMXEVCNTR(イベントカウントレジスタ)に書き込みや読み込みが可能となります。
PMCCNTR (サイクルカウントレジスタ)
このレジスタでは、プロセッサのクロックサイクルをカウントします。
PMXEVTYPER(イベントタイプ選択レジスタ)
このレジスタでは、イベントカウンタを使用してカウントする事象を選択します。Cortex-A/Rシリーズに定義されている事象とプロセッサ固有に定義されている事象があり、イベント番号として定義されています。Cortex-A/Rシリーズ共通のイベント番号は0x00から0x3F、プロセッサ固有のイベント番号は0x40から0xFFです。
イベントタイプ | 機能概要 |
---|---|
0x09 | 取得されたすべての例外をカウントします。 |
0x0A | 例外からの復帰命令をカウントします。 |
0x70 | メイン実行ユニット命令数 (近似値) |
0x71 | 第2実行ユニット命令数 (近似値) |
0x72 | ロード・ストア命令数 (近似値) |
0x74 | NONE命令数 (近似値) |
PMXEVCNTR(イベントカウントレジスタ)
このレジスタでは、PMSELRで選択されたイベントカウンタ値を読み込むことができます。
PMUSERENR(ユーザイネーブルレジスタ)
このレジスタでは、PMUへのユーザモードでのアクセスを設定します。
- 特権モードでは、PMUSERENRは、読み込み・書き込みレジスタです。
- ユーザモードでは、PMUSERENRは、読み込み専用になります。
名前 | 機能 |
---|---|
EN | ユーザモードへのPMUのアクセス設定を行います。 0:PMUへのユーザモードアクセスは不可能です。 1:PMUへのユーザモードアクセスは可能です。 |
こちらも是非
“もっと見る” Cortex-A編
初期化処理
リセット例外からmain()関数を呼び出すまでの初期化は、ユーザが作成する部分とArmコンパイラが実行する部分に分けることができます。コードのコピーや初期化変数/未初期化変数の初期化は、リンカのメモリ配置設定を処理系ライブラリが実行します。
TrustZone(セキュリティ拡張機能)
TrustZoneはCortex-Aシリーズの拡張機能で、大規模OSやアプリケーションが動作するノーマルワールドとセキュリティ関連が動作するセキュアワールドを導入しています。TrustZoneでは、ノーマルワールドメモリ空間とセキュアワールドメモリ空間の分離が可能です。
NEONコプロセッサ
NEONコプロセッサは、リセット時に無効化されるため、初期化処理でアクセス権設定と稼働設定が必要です。NEONコプロセッサが無効状態でNEON命令を実行した場合、「未定義命令例外」が発生します。