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 クロック」という値とは少し差があります。