PIC32MXの動作周波数を確認してみる

Lチカは動いたものの、結局何MHzで動作しているのかよくわからなかったPIC32MXですが、実際の動作周波数を調べてみます。

方法はいろいろありますが、知りたいのはプログラムの実行速度なのでデバッガの表示と実際の速度から推定する手段をとってみます。

まず、ループで時間待ちするプログラムのコードに着目します。

void foo(int index)
{
 register int i;
 for(i=0;i<index;i++);
}
int main(void)
{
 <途中略>
while(1){
 foo(100000);
 mPORTAToggleBits(BIT_0);
 }
return 0;
}

ソースからは1回の点灯期間/消灯期間の間にfoo()という関数が10万回ループすることがわかります。ループ以外の時間はほとんど無視できると思われるのでループに着目します。

デバッガでプログラムをいったんスタートしてブレークし、Disassembly Listingを表示しながらアニメーション実行をさせると、

の赤い四角で囲った部分が「for(i=0;i<index;i++);」のループ内であることがわかります。実際には、最後のbne命令はDelayed Branch命令なので、その次のnopも実行しているはずです。また、ループ内にはlw命令が入っていて、メモリからのリードサイクルが発生することになりますが、頻度は低いのでここではそれが速度の制約にはおそらくならないでしょう。
となると、1ループで5命令を5clkかけて実行することになります。つまり、foo()関数では時間待ちとして約50万clkサイクルを要することになるわけです。

一方で、runモードで実際の実行時間を計ってみると、

H区間/L区間ともに約75msであることがわかりました。ここから計算すると、50万clkサイクルが75msなので、75000000ns/500000=150ns=6.67MHzということになってしまいました。

どうやらconfigビットの設定がおかしいようですので、調べていくと、#pragma config の設定はMPLAB IDEの「Help」→「Topics」→「Language Tools」→「PIC32MX Config Setting」の中にあることがわかりました。

そこで、これを参照してConfigの設定を

#pragma config FPLLMUL = MUL_20, FPLLIDIV = DIV_2, FPLLODIV = DIV_2, FWDTEN = OFF
#pragma config POSCMOD = OFF, FNOSC = FRCPLL, FPBDIV = DIV_2

と修正したところ、H区間/L区間ともに約15msになりました。計算すると、1サイクルが30ns=33.3MHzということになりました。
ちなみにFPLLODIV = DIV_1 のままでは暴走してしまいました。まあ、66.6MHzとなるとしかたないでしょう。

こんどはConfigの設定を

#pragma config FPLLMUL = MUL_20, FPLLIDIV = DIV_2, FPLLODIV = DIV_2, FWDTEN = OFF
#pragma config POSCMOD = OFF, FNOSC = FRC, FPBDIV = DIV_2

と修正すると、内部オシレータ(8MHz)が直接SYSCLKとして使用されるはずですが、H区間/L区間はともに約75ms=6.67MHzに戻りました。

ここまでの結果から考察すると、1ループが5clkで回っているのではなく6clkになっていると考えると辻褄が合うようです。
(75000000ns/600000=125ns=8MHz、15000000ns/600000= 25ns=40MHz)

そこで、「Project」→「Build Option」→「main.c」でCategoriesをOptimizationを選択して、最適化を「0」から「s」に変更してみます。

すると、青の部分をみるとわかるように関数foo()の呼び出し自体が削除されてしまいました。orz・・・
この状態でリアルタイム実行させてみると、370~380nsでトグルしています。これは125nsの3クロック分、ということなのでしょう。 Configの設定を「FNOSC = FRCPLL」に変更すると70~80nsのトグルになりましたので、こちらも25nsの3クロック分、赤で囲んだ部分のループ(Delayed Branchで実行されるnopを含めて3clk)ということであっていそうです。

修正したLチカプログラムをアップロードしておきます。

  • ループでLチカ
    100万回ループで150msの待ち時間を作って、0.3秒周期でLEDが点滅します。
    ただし、最適化をかけてしまうと待ち時間そのものがなくなってしまいます。
    <追伸>SYS_FREQの定数定義が間違っていました(直し忘れ)。修正していませんのでご注意を。
  • 割り込みでLチカ
    タイマー割り込みで100ms周期(1秒間に10回) でLEDが点滅します

いずれもCPUは40MHz動作(SYSCLKは40MHz、PBCLKは20MHz)です。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)