概要
今回はdsPIC33AKシリーズに新搭載されたPMU(Performance Monitor Unit)モジュールの使い方に関して紹介します。
PMUはコード効率を分析する方法を提供し、プロセッサストールを引き起こすソフトウェアルーチンを特定し、最適化する機能を提供します。
関連リンク
| 記事 | リンク | |
| 第1回 | dsPIC33AKシリーズについて | dsPIC33Aシリーズに関して – ぴくおの電子工作的な何かWP (electricpico.com) |
| 第2回 | 開発ボード、コンフィグレーション設定、クロック設定について | dsPIC33AKシリーズ②開発ボード,コンフィグレーション設定,クロック設定について – ぴくおの電子工作的な何かWP (electricpico.com) |
| 第3回 | CPU性能について | dsPIC33AKシリーズ③CPU性能について – ぴくおの電子工作的な何かWP (electricpico.com) |
| 第4回 | FPU性能について | dsPIC33AKシリーズ④FPU性能について – ぴくおの電子工作的な何かWP (electricpico.com) |
| 第5回 | DSPについて | dsPIC33AKシリーズ⑤DSPについて – ぴくおの電子工作的な何かWP (electricpico.com) |
| 第6回 | タイマー1割り込みについて | dsPIC33AKシリーズ⑥Timer1割り込みの使い方 – ぴくおの電子工作的な何かWP (electricpico.com) |
| 第7回(本記事) | PMUの使い方 |
開発環境
開発環境を以下に示します。
| 項目 | 値 | リンク |
| ベースボード | dsPIC33A CURIOSITY PLATFORM DEVELOPMENT BOARD | dsPIC33A Curiosity Platform Development Board User’s Guide (microchip.com) |
| CPUボード | EV68M17A – dsPIC33AK128MC106 Motor Control DIM | dsPIC33AK128MC106 Motor Control Dual In-Line Module (DIM) Information Sheet (microchip.com) |
| 統合開発環境 | MPLAB X IDE v6.20 | MPLAB® X IDE | Microchip Technology |
| コンパイラ | MPLAB XC DSC v3.10 | MPLAB® XC DSC Compiler | Microchip Technology |

PMUについて
dsPIC33AKは5段インターロック命令パイプライン(*1)、投機実行(*2)、条件分岐レイテンシを提言するプリフェッチ分岐予測を特徴とするCPUアーキテクチャを備えています。そのためCPUのクロックとMIPSには一定の関係性が有りません。
CPUのスループットは、以下のような余分なサイクルによって左右されます。
・CPUパイプラインのデータ依存性
・分岐またはプログラムフローの変更
・キャッシュ・ミス・遅いメモリまたはSFRアクセス
・バス・マスター間のアービトレーション
・CPUより遅いバスのアクセス
パフォーマンス・モニターは、プログラム・フローに余分なサイクルが挿入される原因となるイベントと、経過したクロック・サイクル数をカウントします。この情報を使ってCPI(cycles-per-instruction)を計算し、コード効率が悪い原因を突き止めることができます。
このモジュールは、イベント数を捕捉するために8つの独立した64ビット・カウンタを備えています。
| No | イベントソース | 内容 |
|---|---|---|
| 0 | None | |
| 1 | CPUサイクル経過 (参照) | このイベントカウントは、経過したCPUクロックサイクルの総数を提供します。 |
| 2 | CPU命令完了 | CPUパイプライン内の命令が完了したことを示します。 |
| 3 | 書き込みステージFPUストール | “CPUがFPUレジスタに書き込めないため、現在CPU実行が停止していることを示します。これは、FPUが現在既存のレジスタデータの処理に忙しいために発生しました。” |
| 4 | 書き込みステージストール | “RAMまたはSFRへの書き込みの追加待ち時間のために、命令が続行できなかったことを示します。” |
| 5 | 読み取りステージ条件分岐 | “条件分岐命令の発生を示します。条件分岐命令のカウントは、分岐予測の有効性を判断するために、分岐ミス予測の数と比較できます。” |
| 6 | 読み取りステージ分岐ミス予測 | 予測ミスされたプログラムフローの変更によって引き起こされた追加の実行サイクルを示します。 |
| 7 | アドレスステージハザード | “CPUパイプライン内の以前の命令へのデータ依存性によって引き起こされた追加の実行サイクルを示します。この依存性は転送できませんでした。” |
| 8 | アドレスステージFPU命令ストール | “レジスタデータの依存性のために、現在FPUコプロセッサ内の実行が停止していることを示します。” |
| 9 | アドレスステージFPU読み取りストール | “CPUがFPUレジスタから読み取れないため、現在CPU実行が停止していることを示します。これは、FPUが現在レジスタデータを更新しているために発生しました。” |
| 10 | アドレスステージ読み取りストール | “RAMまたはSFRの場所を読み取る追加の待ち時間のために、命令が続行できなかったことを示します。” |
| 11 | アドレスステージストール | “アドレスステージでCPUパイプラインが停止した理由を示します。これは、おそらく命令が破棄されているためです。” |
| 12 | フェッチステージ割り込み遅延カウント有効化 | 割り込み遅延によるサイクル数を示します。 |
| 13 | フェッチステージ読み取りストール | “メモリからプログラムワードをフェッチするために追加のサイクルが必要であることを示します。これは、キャッシュミスや同じメモリからプログラムワードとデータをフェッチする際の仲裁競合が原因である可能性があります。” |
| 14 | フェッチステージプログラムメモリプログラムフロー変更 | “プログラムフローの変更が発生したことを示します。これは、CALL、RETURN、RETFIE、条件分岐または無条件分岐、割り込みイベントが原因である可能性があります。” |
| 15 | フェッチステージプログラムメモリベクトルフェッチ | “CPUが割り込みベクトルをフェッチしており、それがプログラムフローチェンジイベントと整列していることを示します。このイベントは割り込みイベントのカウントに使用できます。” |
| 16 | フェッチステージキャッシュビジー | “キャッシュが命令ストリームバッファ(ISB)からキャッシュメモリにデータを転送している間に、サイクルがビジー状態であることを示します。” |
| 17 | フェッチステージPBUヒット | “要求されたプログラムデータがキャッシュメモリまたはISBから取得されたことを示します。したがって、命令をフェッチするために追加の実行サイクルは必要ありませんでした。” |
| 18 | フェッチステージPBUミス | “要求されたプログラムデータがキャッシュメモリまたはISBから取得できなかったことを示します。したがって、データを取得するためにプログラムメモリから新しいフェッチと追加の実行サイクルが必要でした。” |
PMU 動作確認
ソースコード
コンフィグレーションファイル、クロック設定ファイルは以下のファイルをインクルードしてください。
■コンフィグレーションファイル (config.h)
■クロック設定ソースファイル(Clock_Driver.c)
■クロックヘッダーファイル(clock_driver.h)
ソフトウェアの中身に特に意味は有りません。
メインルーチンでシーケンシャルアクセス、ランダムアクセスを繰り返し、100kHzの割り込みでポートをトグルさせた時の1秒間の実行結果を示します。
#include <stdio.h>
#include <stdlib.h>
#include <xc.h>
#include "config.h"
#include "clock_driver.h"
#include <dsp.h>
#define ARRAY_SIZE 1024 * 2 //
#define ITERATIONS 1000000
int array[ARRAY_SIZE];
int cnt;
// 連続アクセス
void sequential_access()
{
for (int i = 0; i < ITERATIONS; i++)
{
array[i % ARRAY_SIZE]++;
}
}
// ランダムアクセス
void random_access()
{
for (int i = 0; i < ITERATIONS; i++)
{
array[rand() % ARRAY_SIZE]++;
}
}
int main()
{
/*----------------------------------------------------------------------------*/
/*初期化*/
/*----------------------------------------------------------------------------*/
vdg_Clock_Set_Register();
TRISDbits.TRISD9 = 0u;
TRISDbits.TRISD10 = 0u;
// タイマー割り込み設定
PR1 = 1000;
T1CONbits.ON = 1;
T1CONbits.TCKPS = 0;
IFS1bits.T1IF = 0;
IEC1bits.T1IE = 1;
IPC6bits.T1IP = 3;
cnt = 0;
INTCON1bits.GIE = 1;
//PMU設定
HPSEL0bits.SELECT0 = 1U;
HPSEL0bits.SELECT1 = 2U;
HPSEL0bits.SELECT2 = 3U;
HPSEL0bits.SELECT3 = 4U;
HPSEL1bits.SELECT4 = 5U;
HPSEL1bits.SELECT5 = 6U;
HPSEL1bits.SELECT6 = 7U;
HPSEL1bits.SELECT7 = 8U;
HPCCONbits.CLR = 1U;
HPCCONbits.CLR = 0U;
HPCCONbits.ON = 1u;
/*----------------------------------------------------------------------------*/
/*メインルーチン*/
/*----------------------------------------------------------------------------*/
while(1)
{
// 配列の初期化
for (int i = 0; i < ARRAY_SIZE; i++)
{
array[i] = 0;
}
// 連続アクセスの計測
LATDbits.LATD9 = 1u;
sequential_access();
LATDbits.LATD9 = 0u;
LATDbits.LATD10 = 1u;
random_access();
LATDbits.LATD10 = 0u;
}
}
void __attribute__((interrupt, no_auto_psv)) _T1Interrupt(void)
{
cnt ++;
if (cnt > 100000 )
{
HPCCONbits.ON = 0u;
Nop();
}
if ( LATDbits.LATD9 == 1)
{
LATDbits.LATD9 = 0;
}
else
{
LATDbits.LATD9 = 1;
}
IFS1bits.T1IF = 0u;
}
結果
1秒後にカウンタ動作を停止させた時の各カウンタ値を示します。
このように何が原因でスループットが出ないかの調査をすることが簡単にできます。
| イベント | カウンタ |
| CPUサイクル経過 (参照) | 4295167497928 |
| CPU命令完了 | 143244233 |
| 書き込みステージFPUストール | 0 |
| 書き込みステージストール | 0 |
| 読み取りステージ条件分岐 | 11020373 |
| 読み取りステージ分岐ミス予測 | 5557119 |
| アドレスステージハザード | 24305225 |
| アドレスステージFPU命令ストール | 0 |
記事についての注意点
本記事は慎重に内容を検討し正確さに努めておりますが、もし内容に誤りがあったとしても、この記事を参考にして生じた損害等については一切の責任を負いません。

コメント