ARM Cortex-R5(Armv7-R) PMU使い方(パフォーマンス測定方法)


概要

ARM PMU(Performance Monitor)を使った性能測定方法です。
普段、アセンブラ使わない人が書いているので説明が基礎すぎるかも。
こちらに記載した方法で測定した記事が FreeRTOSスケジューリング性能測定 となります。

ドキュメントリンク

Cortex R5 OverView

Architecture Reference Manual

This document is only available in a PDF version. Click Download to view.

とのことで
https://static.docs.arm.com/ddi0406/c/DDI0406C_C_arm_architecture_reference_manual.pdf
ですね。

本題

処理時間をサイクルカウンタで測定

※アセンブラコードについては
http://blog.kmckk.com/archives/2897589.html
を参考にしています。

サイクルカウンタの読み出し

いきなり読んでも値は得られないですが、アセンブラで書くと1行で短く
ARMの仕様書やインラインアセンブラに慣れる意味でも最初に取り上げます。
テクニカルリファレンスマニュアルの PMU Cycle Counter Registerは↓です。
https://developer.arm.com/documentation/ddi0460/d/Events-and-Performance-Monitor/Performance-monitoring-registers/c9--Cycle-Count-Register?lang=en

MRC p15, 0, <Rd>, c9, c13, 0 ; Read PMCCNTR Register

とあるので、これでReadできそうです。

pmu.h
volatile inline static unsigned long __attribute__((always_inline))
pmon_read_cycle_counter()
{
    unsigned long x;
    asm volatile ("mrc  p15, 0, %0, c9, c13, 0": "=r" (x));
    return x;
}

※__attribute__((always_inline))は

GCC does not inline any functions when not optimizing unless you specify the ‘always_inline’ attribute for the function, like this:
https://gcc.gnu.org/onlinedocs/gcc/Inline.html

なので、「最適化レベルが設定されていない場合もインライン展開してね」との意味です。
で、肝心の
"mrc p15, 0, %0, c9, c13, 0": "=r" (x)
ですが、mrcはDDI0406C_C_arm_architecture_reference_manual.pdfから

コプロセッサに移動する。ARM Coreのレジスタ値をコプロセッサに渡す
mcr コプロセッサ名(p0-p15まで), コプロセッサのopecode(0から7まで), コプロセッサに送られるARM coreのレジスタ, コプロセッサレジスタのdestination, 追加のコプロセッサレジスタのdestination, コプロセッサのopecode。省略する場合は0にする

"=r" (x) は出力用オペランドでC言語の変数に渡す値が格納されたレジスタを指定します。
= : 書き込み専用オペランド
r : 汎用レジスタ
ここでは%0が変数xに対応する。
(参考: http://ww2.tiki.ne.jp/~maro/AVR/programing/AvrGccInLineAsm.html)

サイクルカウンタスタート

https://developer.arm.com/documentation/ddi0460/d/Events-and-Performance-Monitor/Performance-monitoring-registers/c9--Count-Enable-Set-Register?lang=en
から

となる。
Control Registerで、E, C, Dを設定する
https://developer.arm.com/documentation/ddi0460/d/Events-and-Performance-Monitor/Performance-monitoring-registers/c9--Performance-Monitor-Control-Register?lang=en
で、カウントスタートして、そのときのサイクル数を返す関数は

pmu.h
/* Performanc e Monitor Control Register of Cortex A9*/
#define PMCR_D 3
#define PMCR_C 2
#define PMCR_E 0
#define PMCNTENSET_C 31
volatile inline static unsigned long __attribute__((always_inline))
pmon_start_cycle_counter()
{
    unsigned long x;
    x = 1 << PMCNTENSET_C;
    asm volatile("mcr   p15, 0, %0, c9, c12, 1" :: "r" (x));

    asm volatile("mrc   p15, 0, %0, c9, c12, 0" : "=r" (x));
    x |= ((1 << PMCR_D) | (1 << PMCR_C) | (1 << PMCR_E));
    x &= ~(1 << PMCR_D); // 3bit目のCycle count dividerは 1:プロセッサーのサイクルそのもの  0:64で割った値 となります。この行を入れると0になります
    asm volatile("mcr   p15, 0, %0, c9, c12, 0" :: "r" (x));

    asm volatile("mrc   p15, 0, %0, c9, c13, 0" : "=r" (x));
    return x;
}

コード内にコメントしましたが
3bit目のCycle count dividerの値で精度が変わるので注意!(1:プロセッサーのサイクルそのもの 0:64で割った値)

サイクルカウンタを使った実行時間の測定

↑の関数を使って↓のような感じで実装

unsigned long start, end;
start = pmon_start_cycle_counter();
// 測定したい処理
end = pmon_read_cycle_counter();
printf("time = %ld", end - start);