新版FM音源プログラム (20)

Cortex-M4 用の結果では、フラッシュのキャッシュ機構の構成が同じ STM32F446 と STM32F407 とがほぼ同じ結果で、別のキャッシュ機構を持つ STM32F303 がやや異なる結果となりました。
PSoC5LP にはフラッシュのキャッシュのヒット/ミスの回数を計測するレジスタがあり、それを利用して測定すると、サイン波テーブルをフラッシュ上に置いた場合と、SRAM 上に置いた場合との差が非常にきれいに現れました。
ARMv7-M アーキテクチャ (Cortex-M3/M4) 用にコンパイルしたアセンブリ言語プログラムのオブジェクトを逆アセンブルしたものを下に示します。

00000000 <calc_slot>:
   0:   b430            push    {r4, r5}
   2:   6802            ldr     r2, [r0, #0]
   4:   6843            ldr     r3, [r0, #4]
   6:   6884            ldr     r4, [r0, #8]
   8:   68c5            ldr     r5, [r0, #12]
   a:   18d2            adds    r2, r2, r3
   c:   8a03            ldrh    r3, [r0, #16]
   e:   6042            str     r2, [r0, #4]
  10:   18a4            adds    r4, r4, r2
  12:   ea13 2314       ands.w  r3, r3, r4, lsr #8
  16:   6942            ldr     r2, [r0, #20]
  18:   5eeb            ldrsh   r3, [r5, r3]
  1a:   f9b0 5012       ldrsh.w r5, [r0, #18]
  1e:   435a            muls    r2, r3
  20:   13d4            asrs    r4, r2, #15
  22:   8b03            ldrh    r3, [r0, #24]
  24:   8244            strh    r4, [r0, #18]
  26:   192d            adds    r5, r5, r4
  28:   436b            muls    r3, r5
  2a:   6083            str     r3, [r0, #8]
  2c:   69c2            ldr     r2, [r0, #28]
  2e:   6a03            ldr     r3, [r0, #32]
  30:   8e85            ldrh    r5, [r0, #52]   ; 0x34
  32:   18d2            adds    r2, r2, r3
  34:   6202            str     r2, [r0, #32]
  36:   fb04 2405       mla     r4, r4, r5, r2
  3a:   8d83            ldrh    r3, [r0, #44]   ; 0x2c
  3c:   6a85            ldr     r5, [r0, #40]   ; 0x28
  3e:   ea03 2314       and.w   r3, r3, r4, lsr #8
  42:   6b02            ldr     r2, [r0, #48]   ; 0x30
  44:   5eeb            ldrsh   r3, [r5, r3]
  46:   435a            muls    r2, r3
  48:   13d2            asrs    r2, r2, #15
  4a:   85c2            strh    r2, [r0, #46]   ; 0x2e
  4c:   3038            adds    r0, #56 ; 0x38
  4e:   3902            subs    r1, #2
  50:   dcd7            bgt.n   2 <calc_slot+0x2>
  52:   bc30            pop     {r4, r5}
  54:   4770            bx      lr

これには、ループ内に

  • ブランチ命令 (2 サイクル) — 1 個
  • ロード命令 (2 サイクル) — 16 個
  • ストア命令 (2 サイクル) — 5 個
  • その他の命令 (1 サイクル) — 14 個

が含まれています。 単純にトータルのサイクル数を計算すると、

2 + (2 × 16) + (2 × 5) + 14 = 58

となります。 Cortex-M3 の場合は MLA 命令の実行に 2 サイクルかかるので、これより 1 増えて 59 サイクルになります。
ARMv7-M アーキテクチャの場合には、ロード/ストア命令を連続させることによりパイプライン化して、実行サイクル数を削減できます。
まず、定数オフセットの STR 命令 (STR R2, [R0, #4] のような場合) は 1 サイクルで実行することができます。 スロット計算プログラムでは、すべてのストア命令は定数オフセットの形になっているので、これで 5 サイクル削減になります。
また、LDR 命令を連続させると、最初の LDR には 2 サイクル必要ですが、後続の LDR はパイプライン化されて 1 サイクルで実行できます。 これによる削減が 9 サイクル分あります。
合計すると、

58 - 5 - 9 = 44

となります。 サイン波テーブルをフラッシュ上に置いた場合は、フラッシュ・レイテンシが 0 の条件で 47 サイクルと、少し差がありますが、サイン波テーブルを SRAM 上に置いた場合には、(フラッシュ・レイテンシが 5 でも) 測定結果が 44 サイクルとなり、サイクル数の計算と一致しました。
これは、サイン波テーブルがフラッシュ上にあると、テーブル・データの読み出しが (たとえレイテンシが 0 であっても) 命令フェッチと競合するためと思われます。
PSoC5LP にはキャッシュのヒット/ミスをカウントすれレジスタがあって、これを利用して測定してみたところ、非常にきれいな結果が出ました。
測定のためのプログラムを下に示します。

  #define N_HIT_MISS (22)

  uint32_t hit_miss[N_HIT_MISS];

. . . . . <中略> . . . . . 

  for (i = 0; i < N_HIT_MISS; i++) {
    CY_SET_REG32(CYREG_CACHE_HITMISS, 0);
    calc_slot(&op_prop[0], NSLOT);
    hit_miss[i] = CY_GET_REG32(CYREG_CACHE_HITMISS);
  } // for (i = 0; ...

対象のレジスタのアドレスはデフォルトでインクルードされるファイルの中で「CYREG_CACHE_HITMISS」として定義されています。

    CY_GET_REG32(CYREG_CACHE_HITMISS)  

で対象のカウンタを読み出すことができ、上位 16 ビットがヒット・カウント、下位 16 ビットがミス・カウントとなっています。 カウントが 16 ビットでの最大値 65535 に達するとラップ・アラウンドはせず、そこで停止するようです。
測定の前には、

    CY_SET_REG32(CYREG_CACHE_HITMISS, 0);

として、カウンタをゼロ・クリアしておきます。
測定プログラムの影響を最小にするために、レジスタを読み取った結果はループ中では hit_miss[] 配列に書き込むだけとし、表示などは後で行なっています。 ループ回数が 22 回となっているのは、結果を 80 桁 × 25 行のコンソールに表示した場合に 1 画面中におさまるように考慮したためです。
まず、サイン波テーブルを SRAM 中に置いた場合の結果を示します。

STAB in RAM
HIT = 362,  MISS = 12, (HIT + MISS) = 374
HIT = 373,  MISS =  2, (HIT + MISS) = 375
HIT = 375,  MISS =  0, (HIT + MISS) = 375
HIT = 375,  MISS =  0, (HIT + MISS) = 375
HIT = 375,  MISS =  0, (HIT + MISS) = 375
HIT = 375,  MISS =  0, (HIT + MISS) = 375
HIT = 375,  MISS =  0, (HIT + MISS) = 375
HIT = 375,  MISS =  0, (HIT + MISS) = 375
HIT = 375,  MISS =  0, (HIT + MISS) = 375
HIT = 375,  MISS =  0, (HIT + MISS) = 375
HIT = 375,  MISS =  0, (HIT + MISS) = 375
HIT = 375,  MISS =  0, (HIT + MISS) = 375
HIT = 375,  MISS =  0, (HIT + MISS) = 375
HIT = 375,  MISS =  0, (HIT + MISS) = 375
HIT = 375,  MISS =  0, (HIT + MISS) = 375
HIT = 375,  MISS =  0, (HIT + MISS) = 375
HIT = 375,  MISS =  0, (HIT + MISS) = 375
HIT = 375,  MISS =  0, (HIT + MISS) = 375
HIT = 375,  MISS =  0, (HIT + MISS) = 375
HIT = 375,  MISS =  0, (HIT + MISS) = 375
HIT = 375,  MISS =  0, (HIT + MISS) = 375
HIT = 375,  MISS =  0, (HIT + MISS) = 375
TOTAL HIT = 8235, TOTAL MISS= 14, TOTAL (HIT + MISS) = 8249

calc_slot() プログラム全体が命令キャッシュに入りきれていない最初の 2 回の呼び出しではキャッシュ・ミスが生じていますが、全体がキャッシュに入ったと思われる 3 回目以降の calc_slot() 呼び出しでは、ミスがゼロで、すべてがヒットしています。
サイン波テーブルは SRAM に置いたので、命令フェッチとは全く競合せず、calc_slot() 呼び出し 1 回当たりの 375 回のフラッシュ読み出し要求はすべて命令フェッチによるものです。
サイン波テーブルをフラッシュ上に置いた場合の結果を下に示します。

STAB in FLASH
HIT = 383,  MISS = 22, (HIT + MISS) = 405
HIT = 405,  MISS =  2, (HIT + MISS) = 407
HIT = 403,  MISS =  4, (HIT + MISS) = 407
HIT = 404,  MISS =  3, (HIT + MISS) = 407
HIT = 401,  MISS =  6, (HIT + MISS) = 407
HIT = 403,  MISS =  4, (HIT + MISS) = 407
HIT = 398,  MISS =  9, (HIT + MISS) = 407
HIT = 405,  MISS =  2, (HIT + MISS) = 407
HIT = 403,  MISS =  4, (HIT + MISS) = 407
HIT = 404,  MISS =  3, (HIT + MISS) = 407
HIT = 403,  MISS =  4, (HIT + MISS) = 407
HIT = 404,  MISS =  3, (HIT + MISS) = 407
HIT = 399,  MISS =  8, (HIT + MISS) = 407
HIT = 402,  MISS =  5, (HIT + MISS) = 407
HIT = 397,  MISS = 10, (HIT + MISS) = 407
HIT = 399,  MISS =  8, (HIT + MISS) = 407
HIT = 397,  MISS = 10, (HIT + MISS) = 407
HIT = 390,  MISS = 16, (HIT + MISS) = 406
HIT = 392,  MISS = 14, (HIT + MISS) = 406
HIT = 395,  MISS = 11, (HIT + MISS) = 406
HIT = 392,  MISS = 14, (HIT + MISS) = 406
HIT = 395,  MISS = 11, (HIT + MISS) = 406
TOTAL HIT = 8774,  TOTAL MISS= 173, TOTAL (HIT + MISS) = 8947

22 回の calc_slot() 呼び出しのすべてでキャッシュ・ミスが起きています。
calc_slot() 呼び出し 1 回当たりのフラッシュ読み出し要求は 407 回で、SRAM の場合の 375 回との差は、

407 - 375 = 32

となり、サイン波テーブルをスロットひとつ当たり 1 回フラッシュから読み出している分に相当していることが分かります。