1. はじめに
この記事では、Performance Counter Unit Core と Intel® FPGA Interrupt Latency Counter Core を活用して、Nios® II の処理時間を測定するサンプルをご紹介いたします。
この記事で紹介する 2 つの IP Core の使用により、システム(Platform Designer のデザイン構成)の変更を抑えつつ、時間計測に必要なロジックを追加実装できます。
Nios® II のソフトウェアに計測処理を追加する対応は必要となりますが、システムへの影響を最小限に測定することができます。
Performance Counter Unit Core については以下の参考コンテンツが公開されていますので、そちらもご参照ください。
参考:Nios II SBT によるソフトウェア開発 セクション3 > 4.コードパフォーマンス検証
2. Performance Counter Unit Core
2-1. 概要
Nios® II の処理速度を測定する方法として、GNU profiler の gprof や Timer Core IP のタイムスタンプを使う方法があります。
GNU profiler の gprof は、Timer Core IP が別途必要で、多くの RAM を消費するためパフォーマンスが低下してしまうデメリットがあります。
また、Timer Core IP のタイムスタンプは、高精度の測定ができますが、Timer Core をシステムに追加する必要があるのとカウント値からの計算ソフトウェアをユーザー側で用意する必要があります。
それぞれについて以下の参考コンテンツがありますので、そちらもご参照ください。
参考:Nios® II 高速化手法 GNU プロファイラーで処理負荷の高い関数を特定
参考:Nios II SBT によるソフトウェア開発 セクション1 ver.14 > 2-3-3. タイムスタンプ・タイマ
Performance Counter Unit Core はソースコード内で指定したセクションの実行時間をシステムクロックの精度で測定することができます。
この IP は、処理速度測定に特化しており、測定用の Nios® II HAL API が用意されていますので、ユーザーは指定された関数を呼び出すだけで、結果の確認が可能です。また、指定したセクションの処理時間だけでなく実行回数もカウントしてくれます。
2-2. Platform Designer における構成とレジスターマップ
【図 1】のように、パラメーター設定でセクションの数を最大7つまで設定することができます。もし、7つ以上測定したい場合は、Performance Counter Unit Core を複数個接続してセクション数を増やすことができます。
なお、サンプル・ソフトウェアでは Nios® II の動作周波数から計算するので、Performance Counter Unit Core の動作周波数は、Nios® II Core と同じものを使用してください。perf_print_formatted_report() に渡す周波数を合わせれば、Nios® II Core と異なっていても正しく計算されます。
【図 1】 Platform Designer の構成
下記リンクにあるレジスターマップを確認するとわかる通り、Performance Counter Unit Core は、
測定開始から終了までを測定する Global Counters と各セクションを測定する Section Counters があります。
(Global Counters はセクションの計測には使用できませんので、任意のセクションの計測が可能な数は「設定セクション数 -1 」になります)
そして、Global Counters と Section Counters に【表 1】のような測定時間を格納する Time Counter と実行回数を格納する Event Counter がそれぞれ用意されています。
また、カウント値は実行回数分蓄積されるので、表示される数値は合計値になります。
参考:36. Performance Counter Unit Core > Table 365. Performance Counter Core Register Map
【表 1】 Global Counters と Section Counters にそれぞれ用意されるカウンター
カウンタの種類 | Bit 幅 | 概要 |
Time Counter | 64bit | 指定したセクションの実行時間をシステムクロックの精度で測定 |
Event Counter | 32bit | 指定したセクションの実行回数を測定 |
2-3. Performance Counter Unit Core の Nios® II HAL API
ソフトウェアの実行手順等はサンプル・ソフトウェアや下記の参考コンテンツをご参照ください。
参考:Nios II SBT によるソフトウェア開発 セクション3 > 4-1-2. パフォーマンス・カウンタの使用例
【表 2】に Performance Counter Unit Core の Nios® II HAL API を一覧で記載します。これらの関数を使用するには、altera_avalon_performance_counter.h を インクルードで定義することで使用できるようになります。
【表 2】 Performance Counter Unit Core の HAP API 一覧
Nios® II HAL API | 概要 |
Performance Counter Unit Core 制御用関数 | |
PERF_RESET (hw_base_address) | すべてのカウンターを停止し、カウンター値を 0 にします |
PERF_START_MEASURING (hw_base_address) | Global Counter をスタートさせ、Section Counter を有効にします |
PERF_STOP_MEASURING (hw_base_address) | Global Counter を停止させ、Section Counter を無効にします |
PERF_BEGIN (hw_base_address,which_section) | 各セクションの開始位置にて実行して、Section Counter をスタートさせます |
PERF_END (hw_base_address,which_section) | 各セクションの終了位置にて実行して、Section Counter を停止しさせます |
レポート関数 | |
perf_print_formatted_report ( |
フォーマット化された測定結果を stdout に出力します |
perf_print_formatted_report () 内部で使用される関数 | |
perf_get_total_time (void* hw_base_address) | Global Counter の合計値をクロックサイクル数で返します |
perf_get_section_time (void* hw_base_address, int which_section) |
指定したセクションの合計値をクロックサイクル数で返します |
perf_get_num_starts (void* hw_base_address, int which_section) |
指定したセクションの実行数を返します |
alt_get_cpu_freq () | 動作周波数を Hz で返します |
* hw_base_address:Performance Counter Unit Core のベースアドレス(system.h に定義されています)
* which_section:カウンターセクションの番号
* clock_freq_hertz:動作周波数(system.h に定義されています)
* num_sections:使用したセクションの数
* section_name_1, section_name_n:レポートに表示したいセクション名
2-4. 割り込みハンドラーの測定に関して
Performance Counter Unit Core の PERF_BEGIN() と PERF_END() は割り込みハンドラー内で使用することができます。従って、割り込みハンドラー内部の処理時間を測定する際に使用することができます。
但し、レポート関数(perf_print_formatted_report()) は stdout を使うため、割り込みハンドラー内で使用しないでください。
3. Intel® FPGA Interrupt Latency Counter Core
3-1. 概要
Interrupt Latency Counter Core(ILC)は、割り込み信号がアサートされてから、ディアサートされるまでの時間をシステムクロックの精度で測定することができます。
いつ起こるかわからない割り込みの処理時間を測定できるので、時間のかかっている割り込み処理を特定したい時など、デバッグ時に入れておくと便利です。
0~31 までの割り込み番号それぞれにカウンター値を保存できるレジスターが用意されているため、割り込み番号ごとに測定することができます。割り込み信号がアサートされている間の時間なので、割り込みハンドラーの呼び出し時間も含めた測定ができます。
また、この IP には Nios® II HAL API が用意されていないため、カウント値を読み出して時間に変換する処理はユーザーが用意する必要がありますが、結果を出力する関数をサンプル・ソフトウェアとして用意しました。
ILC は、割り込み信号の状態でカウントアップを開始するので、IP のスタートとストップのソフトウェア処理をシステムの最初と最後だけに入れておくだけで測定できます。
Level 割り込み信号と、Edge/Pulse 割り込み信号の両方に対応していますが、Nios® II の割り込みは Level 割り込みですので、今回は Level 割り込み信号でのサンプルをご紹介しています。
3-2. Platform Designer における構成とレジスターマップ
【図2】のように、Nios® II の割り込み信号との間に Interrupt Latency Counter Core を挿入します。
Interrupt_Type は Level に設定し、使用する割り込み信号数に合わせて IRQ_Port_Count を設定します。
動作周波数は Nios® II と同じものを使用します。
Nios® II IRQ 番号と Interrupt Latency Counter Core の IRQ 番号は同じになるように設定します。
JTAG UART は、デフォルト設定では割り込みを使用して stdio の処理(printf() の処理)を実行するので、測定対象から外します。
【図 2】 Platform Designer の構成
【表 3】は Interrupt Latency Counter Core のレジスターマップになります。一部補足を次で解説します。
【表 3】 Interrupt Latency Counter Core レジスターマップ(全て 32bit レジスター)
オフセット | レジスター 名 | R/W | 概要 |
0x0 | IRQ_0 Latency Data Registers | R | IRQ0 のカウント値格納レジスター |
0x1 | IRQ_1 Latency Data Registers | R | IRQ1 のカウント値格納レジスター |
... | ... | ... | ... |
0x1F | IRQ_31 Latency Data Registers | R | IRQ31 のカウント値格納レジスター |
↓↓ 制御用 レジスター ↓↓ | |||
0x20 | Control Registers | R/W | bit [0] 以外は、Read Only です(3-2-1 で補足) |
0x21 | Frequency Registers | R | 設定された周波数を確認できます |
0x22 | Counter Stop Registers | R/W | パルス割り込み信号の時のみ使用します(3-2-2 で補足) |
0x23 | Read Data Valid Registers | R | Latency Data Registers からデータを Read 出来る状態になるとアサートされます(3-2-3 で補足) |
0x24 | IRQ Active Registers | R | IRQ 信号の状態を確認できます |
3-2-1. Control Registers
Control Registers は【表 4】の構成になっています。
bit [0] の Global Enable は測定を開始する際に必ず 1 に設定する必要があります。
その他の Bit は Platform Designer で設定した情報が格納されています。
【表 4】 Interrupt Latency Counter Core の Control Registers 構成
Field Name | ILC Version | IRQ Port Count | IRQ Type | Global Enable |
Bit Location | 31 ~ 8 | 7 ~ 2 | 1 | 0 |
R/W | R | R | R | R/W |
概要 | ILC のリビジョン情報が 格納されてます |
設定した IRQ 信号数が 格納されています |
設定した割り込みタイプ 0:Level |
ILC を動作させるには、 必ず 1 にセットしてください |
3-2-2. Counter Stop Registers
Counter Stop Registers は、IRQ Type が Edge/Pulse の場合に使用します。
Edge/Pulse 割り込み信号の場合、割り込み信号がディアサートされるタイミングがわからないため、割り込みハンドラーの最後に Counter Stop Registers に 1 を書き込む処理を入れて、明示的にカウントを止めます。次の割り込みを測定したい場合は、Counter Stop Registers をクリアする必要があります。
IRQ Type が Level の場合は使用しません。
3-2-3. Read Data Valid Registers
Read Data Valid Registers は、Latency Data Registers からデータを読み取れるか判断するためのレジスターです。Latency Data Registers からデータを読み出す前に、Read Data Valid Registers の状態を確認してから読み出すような処理を実装してください。Read Data Valid Registers の値が High になっていれば、データを読み取る準備が整っています。
カウンターがストップしてから、3 Cycle 後に Read Data Valid Registers が High になる仕様となっています。
3-3. Intel® FPGA Interrupt Latency Counter Core の ソフトウェア記述方法
IRQ Type によってソフトウェアの記述方法が異なります。
3-3-1. IRQ Type が Level の場合
手順は下記のようになります。
-
-
- Control Registers の bit [0] Global Enable を 1 に変更
- 測定が完了したら、Read Data Valid Registers を読み取って、Latency Data Registers から有効データを取得
- Latency Data Registers の値は、システムクロックから算出されたクロックサイクル数
-
3-3-2. IRQ Type が Edge/Pulse の場合
手順は下記のようになります。
-
-
- Control Registers の bit [0] Global Enable を 1 に変更
- 割り込みハンドラーの最後に、Counter Stop Registers の該当する Bit に 1 を書き込んで、カウントを停止させる
- 測定が完了したら、Read Data Valid Registers を読み取って、Latency Data Registers から有効データを取得
- Latency Data Registers の値は、システムクロックから算出されたクロックサイクル数
- 次の割り込みを測定したい場合は、Counter Stop Registers に 0 を書き込む
-
3-4. 使用にあたっての注意点
- Nios® II システム動作開始から IRQ 信号が一度もアサートされていない状態で、Latency Data Registers を Read しないようにしてください。Waitrequest 信号がアサートされっぱなしになり、Nios® II システムが停止してしまうことがあります。
- Latency Data Registers がオーバーフローしても通知する機能はありませんが、非常に大きい値が読み取れた場合は、割り込みハンドラー処理で Nios® II システムが停止してしまっていると判断することができます。
4. サンプルデザイン&サンプル・ソフトウェアの概要
この記事で紹介するサンプルデザイン情報を以下に記載します。
・使用 KIT:Cycloen V GX スタータ開発キット(ただし、大半は Qsys ファイルの移植で動作可能)
・Tool バージョン:Quartus® Prime ver.19.1_Standard
・主な使用 IP:
Nios® II, On-Chip RAM, JTAG UART, PIO(LED, Switch),
Interrupt Latency Counter, Performance Counter
・Nios® II/f(Gen2)
命令キャッシュ: 4 KB
データキャッシュ: 4 KB
・ On-Chip RAM:128KB(ワークメモリー)
Platform Designer の接続図を【図 3】に示します。
【図 3】 サンプル Platform Designer 接続図
<ソフトウェア解説>
このサンプル・ソフトウェアは、Performance Counter と Interrupt Latency Counter の測定結果を表示します。Performance Counter では、usleep 関数を入れた処理と IRQ 2 番の PIO Input Core の割り込みハンドラーの処理時間を測定し表示します。Interrupt Latency Counter では、IRQ 2 番がアサートされてからディアサートされるまでの処理時間を測定して表示します。
用意したソフトウェアは 3 つです。
・Performance_Counter_and_ILC_Sample.c :main 関数と割り込みハンドラーを記載
・Interrupt_Latency_Counter.h :ILC のレジスター情報を記載
・Interrupt_Latency_Counter.c :ILC の結果表示関数を記載
下記 Define で定義されている定数を変更することで、ご自身の Platform Designer の構成に合わせて使用可能なソフトウェアになっています。
/***********************************************************************************
* re-definitions
* ※このセクションは"system.h"で定義された定数を、
* 本ソースファイル内で使用するために再定義したものです。
* ご使用の際は右側の定数を"system.h"を参照の上、変更してお使いください。
***********************************************************************************/
#define LED_CSR_ADR (LED_BASE) // LED のアドレス
#define KEY_BIT_MSK ((1 << KEY_DATA_WIDTH) -1) // KEY のビットマスク
#define KEY_CSR_ADR (KEY_BASE) // KEY のアドレス
#define KEY_IRQ_ID (KEY_IRQ_INTERRUPT_CONTROLLER_ID) // KEY の割り込みコントローラID
#define KEY_IRQ_NO (KEY_IRQ) // KEY の割り込み番号
#define PERF_COUNT_ADR (PERFORMANCE_COUNTER_BASE) // パフォーマンスカウンタのアドレス
#define PERF_SECTIONS (PERFORMANCE_COUNTER_HOW_MANY_SECTIONS) // パフォーマンスカウンタのセクション数
#define ILC_ADR (INTERRUPT_LATENCY_COUNTER_BASE) // ILC のアドレス
/***********************************************************************************
<サンプルの動作>
Nios® II SBT の Run As で実行すると、Work RAM(このデザインでは、On Chip RAM)で Nios® II が実行されます。
LED が 0xAA ⇒ 0x55 ⇒ 0xAA ⇒ 0x55 と移行している間(約 10 秒間)に、KEY スイッチを押すと割り込みが入ります。
一回 KEY スイッチを押した結果を以下に記載します。
Nios II Start
--Performance Counter Report--
Total Time: 10.5602 seconds (528009922 clock-cycles)
+---------------+-----+-----------+---------------+-----------+
| Section | % | Time (sec)| Time (clocks) |Occurrences|
+---------------+-----+-----------+---------------+-----------+
|Init |7.12e-05| 0.00001| 376| 1|
+---------------+-----+-----------+---------------+-----------+
|usleep 1s | 9.09| 0.96003| 48001419| 1|
+---------------+-----+-----------+---------------+-----------+
|For Loop | 90.9| 9.60016| 480007863| 5|
+---------------+-----+-----------+---------------+-----------+
|KEY IRQ |7.95e-06| 0.00000| 42| 1|
+---------------+-----+-----------+---------------+-----------+
|Unused | 0| 0.00000| 0| 0|
+---------------+-----+-----------+---------------+-----------+
|Unused | 0| 0.00000| 0| 0|
+---------------+-----+-----------+---------------+-----------+
--Interrupt Latency Counter Report--
IRQ Num : 32
+---------------+-----------+---------------+
| IRQ | Time (sec)| Time (clocks)|
+---------------+-----------+---------------+
| IRQ 0 | 0.00000000| 0|
+---------------+-----------+---------------+
| IRQ 1 | 0.00000000| 0|
+---------------+-----------+---------------+
| IRQ 2 | 0.00000438| 219|
+---------------+-----------+---------------+
| IRQ 3 | 0.00000000| 0|
+---------------+-----------+---------------+
IRQ 4 ~ 30 は割愛 (全て 0.00000000 sec)
+---------------+-----------+---------------+
| IRQ 30 | 0.00000000| 0|
+---------------+-----------+---------------+
| IRQ 31 | 0.00000000| 0|
+---------------+-----------+---------------+
Nios II Finish
5. まとめ
Nios® II の処理時間を測定する方法はいくつかありますが、Platform Designer に標準で用意されている Performance Counter Unit Core や Interrupt Latency Counter Core(ILC)を使用することで、System への影響を最小限に抑えて測定できますので、デバッグ時に是非ご活用ください。
5-1. Internal Interrupt Controller(IIC)と Vectored Interrupt Controller(VIC)について
サンプルソフトウェアの結果 を確認するとわかる通り、割り込みハンドラー(Performance Counter Report [KEY IRQ])よりも、割り込みレイテンシー(Interrupt Latency Counter Report [IRQ 2])で多くのクロックを消費していることがわかります。
今回のサンプルの Nios® II 割り込み処理は、Internal Interrupt Controller(IIC)で実装しており、割り込みハンドラーに飛ぶまでの処理をソフトウェアで実行しているためより多くのクロックを消費します。
参考:Nios® II Software Developer Handbook > 9.5.3. Hardware Interrupt Funnel
Internal Interrupt Controller(IIC)は割り込み処理に時間がかかると感じた方は、Vectored Interrupt Controller(VIC)をご検討ください。
IIC は割り込み処理をソフトウェアで実行しているのに対して、VIC はハードウェアで割り込み処理を実行するため、少ないクロック数で割り込みハンドラーに到達させることができます。
VIC の導入方法とサンプル、および、IIC と VIC の比較については下記のコンテンツをご参照ください。
参考:Nios® II - Vectored Interrupt Controller の実装手法