平成26年9月27日
キャッシュ機能の疑問
先の確認では内臓のフラッシュROMがウエイトなしで動作できる20MHzの周波数で期待通りの速度で動作させることが出来ました。
ところが動作周波数を80MHzにまであげると暴走してしまうようです。
真っ先に疑うのはキャッシュ機能に関係したレジスタの設定ミスです。それ程数が多い訳ではないのですが、特にキャッシュ機能を禁止しているような設定は見当たりません。仕方が無いのでXC32に付属のライブラリ関数で何かヒントがないかを探してみました。
周辺機能関連のライブラリソースはpic32-libs/peripheralの下に各機能ごとにフォルダ分けしてあります。その中にpcacheというフォルダがキャシュ関連のソースのようです。調べてみるとCheKseg0CheOn()という関数が見つかります。どうやらキャッシュ機能は周辺機能レジスタではなくCP0(コプロセッサ)によって動作の許可・禁止が行われるようです。データシートに戻ってみるとセクション2.
M4Kコアの説明でわずかな記述がみられますが、どう見てもキャッシュ機能の説明部分で何一つ説明がないのはおかしい。少なくとも関連項目として触れて欲しい内容です。
これに従ってプログラムを修正します。
#include <xc.h>
#include "plib.h"
#pragma config FPLLMUL = MUL_20, FPLLIDIV = DIV_4, FPLLODIV = DIV_1, FWDTEN = OFF
#pragma config POSCMOD = HS, FNOSC = PRIPLL
int main(void)
{
CheKseg0CacheOn();
CHECONbits.PFMWS = 0;
TRISD = 0;
LATD=0;
for(;;){
LATD=0xffff;
LATD=0;
}
return 0;
}
結果は、だめでした。他にも設定が必要な項目があるのか??
次にネットでPIC32のキャッシュ関係で検索すると有名な
後閑氏のHPがヒットします。
ここでも新たな
関数SYSTEMConfigPerformance()が出てきます。この関数に引数としてCPUの動作周波数を与えるだけで、キャッシュやPBCLKの設定をしてくれるそうです。
PBCLKの設定 ???
何でPBCLKを設定する必要があるの?
キャッシュに関する設定はそれ以外に影響が出ないので最適化してくれるのは有難いが、PBCLKは周辺機能全体の動作速度を変えてしまう非常に重要な設定です。勝手に変えられても、それ以下の周辺機能の大半の設定を書き直す羽目になるだけです。
最悪、この関数を実行した後から再度PBCLKを変更しなおすとして、まずはこの関数によってキャッシュ機能が正しく動作するか確認します。プログラムは下記になります。
#include <xc.h>
#include "plib.h"
#pragma config FPLLMUL = MUL_20, FPLLIDIV = DIV_4, FPLLODIV = DIV_1, FWDTEN = OFF
#pragma config POSCMOD = HS, FNOSC = PRIPLL
int main(void)
{
SYSTEMConfigPerformance(80000000);
TRISD = 0;
LATD=0;
for(;;){
LATD=0xffff;
LATD=0;
}
return 0;
}
確かに期待通りポートDの'1'出力時間は12.5ns(80MHz)まで高速化します。SYSTEMConfigPerformance()関数を使用することで、キャッシュ機能を有効化できることが確認できました。
念のため、この関数実行後にPBCLKの分周を決定するPBDIVの値を確認すると初期値3から0に変更されています。この状態でPBDIVを3に戻してもキャッシュ動作に影響はありません。このことからPBCLKを任意の値で使用する場合は、先にSYSTEMConfigPerformance()関数を実行して、その後にPBDIVの値を書き換えできることがわかります。ただし、「現時点では」の但し書き付きです。
これでいいのか?
システムの組み込み関数を使用して、不都合な部分の機能を後書きで処理するというのはあまり褒めたものではありません。
この関数のソースを調べて何をやっているのかを確認しておくべきです。この辺りはデバイスのハードウェアを直接操作する組み込み装置では重要です。不用意に組み込み関数が設定したレジスタを上書きしてしまうリスクがあります。
調べてみると、この関数はインラインマクロとして定義されていました。pic32-libs/include/peripheralにあるsystem.hに本体があります。
処理内容は
- PBCLKの設定はSYSCLKが80MHzを超える場合にはPBDIVを2に、それ以外は1に設定する。データシートからは見つけることが出来なかったのですが、周辺バスの駆動周波数は80MHzが上限のようです。
- RAMへのアクセスをデフォルトの1ウエイトから0ウエイトへ変更
- ROMへのアクセスを動作周波数に応じたウエイト数に設定
- キャッシュ機能への設定としては以下を行っています。
- CHECONレジスタのPREFENビットを3(キャッシュ可能領域とキャッシュ不可能領域の両方に対して予測プリフェッチを有効にする)に変更しています。
- CP0レジスタ内にあるキャッシュ有効ビットをセット
これだけの処理が必要なことをデーターシートから読み取るのは非常に大変です。たとえデーターシートを全部読んでいたとしてもどれだけ気付くことが出来たかと思います。
- PBCLKの上限周波数の規定を未だにデーターシートから見つけられない。
- RAMへのウエイト挿入の設定はバスの制御を管理するBMXCONレジスタの中にあるので見つけにくい。
- ROMへのウエイト挿入の設定はキャッシュ制御を管理するCHECONレジスタの中にあるので見つけにくい。
- キャッシュを有効にするために上記(CHECONレジスタのPREFENビットおよびCP0への設定)の二つの処理が必要であることをデータシートの記述から読み取ることは非常に難しい。
これらを見る限りデバイスのデーターシートだけで正しい処理内容にたどり着くのは現実的には無理だろうと感じます。サンプルプログラムやアプリケーションノートといった文書によって正しい設定方法が示されていることが必要です。データーシートを熟読することも重要ですが、全てのレジスタの意味が誤解のないレベルで理解できるようには書かれていないのも事実で、最終的には実績のあるプログラムを真似ることも必要になってきます。これがバグの温床になるとして批判を受けることもあるのですが、現実問題として不十分なドキュメントのみからプログラムを起こせばバグが無くなる訳ではなく、むしろもっと酷いバグを抱え込む要因にさえなりかねない。マイコンを使用する上での悩ましい問題です。
なお、SYSTEMConfigPerformance()関数を使った後にPBDIVを書き換える行為は、関数の処理内容からみて問題ないことが分かります。これで安心してSYSTEMConfigPerformance()関数を使うことが出来ます。