新版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 回フラッシュから読み出している分に相当していることが分かります。