LPC1114FN28/102 (9) -- IRQLATENCY レジスタ (2)
SysTick タイマは、24 ビット幅のダウン・カウンタによる簡単なインターバル・タイマで、一定周期で CPU に割り込みをかけるのを目的としています。
コア・ペリフェラルとして ARM 社から供給される IP 内に含まれるので、半導体ベンダ独自の周辺モジュールのタイマとは異なり、各メーカーによる違いはありません。
ベンダが独自に作成して供給しない限り、SysTick タイマの初期設定用の関数も ARM 社が供給する CMSIS (Cortex Microcontroller Software Interface Standard) 内の「SysTickConfig()」関数を共通に利用します。
使い方は簡単で、例えば設定したい割り込み周期を 256 コア・クロックとすると、
SysTickConfig( 256 );
を実行すれば、SysTick タイマの各レジスタが初期設定され、必要な割り込みの設定も行ってくれます。
SysTick タイマはダウンカウンタなので、前回の図のカウント値を表す「のこぎり波」は上下逆となり、下降する斜辺を持つ形となります。
カウンタの値が 1 → 0 へ変化するタイミングで割り込みが発生し、次のクロック・サイクルでカウンタ (SysTick->VAL) の値は、リロード値レジスタ (SysTick->LOAD) に保持されている値に再設定されます。
したがって、現在のカウント値を SysTick->VAL レジスタから読み取って、SySTick 割り込み周期 (SysTick->LOAD + 1) から差し引けば割り込み発生からのコア・クロック数が計算できます。
フラッシュ読み出しを 0 ウェイト・ステートで行うために、CPU クロック周波数を下げて 0 ウェイト・ステートで動作可能にします。
デフォルトのスタートアップ・ルーチンでは PLL を使って CPU クロック周波数 (HCLK) を 48 MHz に設定していますが、簡単のため、単に PLL 出力を 4 分周して HCLK = 12 MHz とします。
具体的なプログラムの断片を下に示します。
LPC_SYSCON->SYSAHBCLKDIV = 4; LPC_FLASHCTRL->FLASHCFG &= (~0x03); // 0 WS
SysTick 割り込みハンドラは次のようにしました。
#define SYSTICK_PERIOD (256) #define N_hist (0x100000UL) #define hist_range (256) volatile uint32_t hist[hist_range] = {0, }; volatile uint32_t hist_count = 0; volatile uint32_t hist_end = 0; void SysTick_Handler( void ) { volatile register uint32_t tc_save; tc_save = SysTick->VAL; // remember systick count if (N_hist > hist_count) { tc_save = (SYSTICK_PERIOD - tc_save); if (hist_range > tc_save) { hist[tc_save]++; } // if (hist_range > tc_save) { ... hist_end = (N_hist == (++hist_count)); } // if (N_hist > hist_count) { ... } // void SysTick_Handler( void )
割り込みは SysTick 割り込みだけとし、ハンドラの冒頭部分でレジスタ変数「tc_save」に SysTick タイマの現在のカウント値を読み込みます。
次に、割り込み発生からのクロック・カウント数を計算し、ヒストグラムを作成します。
N_hist 回のデータを収集したらヒストグラム作成は終了し、「hist_end」フラグを立ててメインルーチン側にヒストグラム作成完了を伝え、メイン側でヒストグラムを表示します。
ここでは N_hist = 0x100000 = 1,048,576 回としています。
Keil/ARM MDK-Lite uVision V4.20 でコンパイルした結果の、SysTick ハンドラ部分の逆アセンブル・リストを下に示します。
000000f0 <SysTick_Handler>: f0: 480d ldr r0, [pc, #52] (128) f2: 6980 ldr r0, [r0, #24] f4: 490d ldr r1, [pc, #52] (12c) f6: 680b ldr r3, [r1, #0] f8: 2201 movs r2, #1 fa: 0512 lsls r2, r2, #20 fc: 4293 cmp r3, r2 fe: d212 bcs.n 126 . . . . . < 中略 > . . . . . 126: 4770 bx lr 128: e000e000 .word 0xe000e000 12c: 10000000 .word 0x10000000 130: 10000020 .word 0x10000020
C ソースでの、
tc_save = SysTick->VAL;
が
f0: 480d ldr r0, [pc, #52] f2: 6980 ldr r0, [r0, #24]
という 16-bit thumb 命令ふたつにコンパイルされています。
最初の ldr 命令で、0x0128 番地に格納されているコア・ペリフェラル・レジスタ・ブロックの開始アドレス 0xe000e000 を PC 相対アドレシングで r0 にロードしています。
2 番目の ldr 命令で SysTick->VAL レジスタの値を r0 に読み出しています。
イミディエート・オフセット値の 24 = 0x18 をベースアドレスに加えた、
0xe000e000 + 0x18 = 0xe000e018
が SysTick->VAL レジスタの絶対アドレスです。
どちらも実行に 2 クロックかかるので、この部分で合計 4 クロックを要して SysTick タイマ・カウントを読み込むことになります。
このプログラムを LPC1114FN28/102 で実行した結果、1,048,576 回のうち1,048,576 回、つまり 100 % の頻度で 26 クロックとなりました。
レイテンシの変動はない、つまり、「ジッタ」はゼロということになります。
これは、次回説明しますが、IRQLATENCY レジスタがリセット後のデフォルト値 0x10 = 16 の設定のままでの結果ということになります。
ldr 命令ふたつの実行に要するクロック数 4 を差し引いても 22 クロックで、期待される「16 クロック」という値とは少し差があります。