最近よく使われている新世代のPIC「Enhanced Mid-Range CPU」。おなじみのPIC16Fシリーズ(14ビットコア)の改良版について調べました。
周辺機能が強化され、集積度が上がったので、メモリー(ROM/RAM)や周辺回路が盛り沢山なのですが、ここでは、主にCPUの機能について、マニュアルを元に書いておきます。
従来は割り込みの出入り口では、最低限の事しか行われませんでしたが、改善されました。
従来、割り込み時に行われていたのは、割り込み禁止フラグの設定と戻りアドレスのセーブだけ。STATUSの退避とか、普通最低行われそうなことも行われていなかったし、STATUS変更無しに行える命令があまりないので、割り込みのプロローグとエピローグは、とっても変な処理を書かざるを得ませんでした。
今回の変更では、主だったレジスタ( W,STATUS,BSR,FSR0,FSR1,PCLATH )が自動退避されるようになりました。退避はスタックに行うのではなく、シャドーレジスタに行われる方法。切り替えで済むので、時間がかからないし、多重割り込みも、多分、しないので、妥当な選択でしょう。
あの変な割り込みコードは書かなくて済むようになったわけです。
そうそう、スタックも16段に増えました。
大きな変更の最初は、専用のバンク切り替えレジスタが備わったこと。容量128バイトのバンクが32個(もちろんチップによって実装ページ数は違いますが)。従来はSTATUSレジスタにあったビットでバンクを切り替えていたのですが、今回は BSRレジスタで切り替えます。
バンクの構成もすっきりして、バンクの先頭(00H-1FH)がコアレジスタと機能レジスタ領域、真ん中(20H-6FH)が汎用メモリ、末尾(70H-7FH)がコモン領域(どのバンクからも同じレジスタ)です(ただし、バンク32は特殊で割り込み時の退避用シャドーレジスタが置かれています)。
コアレジスタは、普通のCPUならば内蔵レジスタとでも言うべきレジスタで、全部で12本あります(INDF0,INDF1,PCL,STATUS,FSR0L,FSR0H,FSR1L,FSR1H,BSR,WREG,PCLATH,INTCON)。どのバンクからも同じアドレスでアクセスできます。
機能レジスタ(Special Function Registers)は、デバイス制御のためのレジスタで、バンクごとに様々な機能が置かれています。当然、チップが異なれば実装も変わります。
汎用メモリは、普通のRAM。これも、チップによってサイズが変わる部分。バンクの若い方から詰めて実装されているようです。
コモン領域は、各バンク16バイトを占めていますが、すべてが、バンク0の最後の16バイトと同じものです。バンク切り替えに関係なく使いたいような作業域をここに置きます。間接アドレッシングに使うFSRが二本になりました。FSRは2バイトの大きさになり、すべてのデータメモリを指定できるようになりました。STATUSレジスタのお世話にならなくて済むのは嬉しいです
FSRが二つだから、入力、出力用にインデックスレジスタとして使えます。また、限られた命令だけですが、自動INC / 自動DEC ができるようになったし、FSR+オフセットでアドレスできます。FSRの2バイトにまとめてオフセット演算もサポートされて、とても便利になりました。
FSRは2バイトなので、0000h〜ffffhの広いアドレスが分不相応に割り付けられています。この広大なアドレス空間を利用して、ちょっと複雑で便利なマッピングがされています。
細かく内容を眺めてみます。
BSRとレジスタアドレスの組み合わせで指定するアドレスと同じものをFSR(L,H)で指定します。ただし、ちょっと変換が必要。というのは、レジスタアドレス空間は7ビットしかないので、連続アドレスにするために、全体を右詰めしてアドレスを指定しなければなりません。まぁ、理屈なんですけど、気をつけなきゃね。
FSRnLには、7ビットのレジスタアドレスと、最上位ビットにBSRの最下位ビットをセット。FSRnHには、BSRを右シフトしてからセット。ということになります。
汎用データメモリを連続領域としてFSRを通じてアクセスできるようになりました。汎用のデータメモリ(レジスタ)は各バンクの真ん中に80バイトあるのですが、通常のデータメモリ空間では飛び飛びの位置になっています。これでは、80バイトを越える連続領域を素直に扱えません。これを飛び飛びではなく、連続領域としてアクセスできるようになりました。200バイトの連続領域など、従来はできなかったことができるようになったわけです。
実現方法はワイルドで、問題の各バンクの真ん中80バイトを取り出して、バンクの順に並べてしまうというもの。これが、FSRのアドレス2000h〜29afhに割り付けられました。容量は最大で31バンク分。最後のバンクはシャドーレジスタに使っているため除外されているようです。
便利になった反面、通常のアクセスアドレスと、この連続アクセスではアドレスが違うので、アドレスの変換など、面倒なトラブルも起きそうな気がします。バッファ以外では難しいかも。
プログラムメモリの下位バイトも、FSRから間接指定で読めるようになりました。プログラムメモリからデータを取り出すのは、結構面倒なプログラムを書いていたのですが、普通に読み出しできます。まるでPICではなくなったみたい。
アドレスの指定は簡単で、プログラムメモリの(ワード)アドレスの最上位ビットを1にしたものをFSRにセットするだけです。
データメモリはバンク切り替えがすっきりしたのですが、プログラムメモリの方は、あまり改善されていません。Goto/Call命令では相変わらずPCLATHの上位部分を使うので、遠くに飛ぶときは気をつけなければいけないのは、従来のとおり。プログラム空間そのものは32KWに拡張されたのは嬉しいのですが、扱いにくいさは以前のまま。2KBの壁は意識せざるを得ません。
といっても何点かの改善はなされています。
PICはバイト単位でレジスタをアクセスするため、2バイト長のPC(プログラムカウンタ)を書き換えるために、特殊な構造をとっています。アドレスの上位をPCLATHに書き込み、次にPCLにアドレスの下位を書くとPCLATHの値も一緒にPCにセットされて、めでたく、任意アドレスへの分岐ができるわけです。とってもワイルド!
新しいPICでも、この部分は変わっていません。アドレスが最大15ビット長となったのでPCLATHに書き込む有効なビット数が増えただけです。
GOTO / CALLの動作も有効アドレスが長くなった以外は、従前のものと変わりがありません。つまり、使いにくさは保存されたわけです。
GOTO / CALL 命令に書き込まれるアドレスは11ビット。これだけでは2KWの範囲への分岐しかできません。そこで、GOTO / CALL 命令を実行するとき、アドレスの足りない部分をPCLATHから持ってきて補い、そこへ分岐するようになっています。具体的には、PCLATHの b6 - b3 を使用します。
ちょっと複雑そうですが、実際は単純で、PCLATHにはアドレスの上位バイトを書き込むのですから、その下位3ビットが無視されて使われるだけです。
面倒なのは、遠くへ飛ぶ前には必ず PCLATH の操作が必要なこと。アセンブラとリンカでは、PAGE擬似命令を使って面倒な計算などしてくれますが、このときは、不要な場合にもコードが出てしまいます。メモリの割り当てを考慮して書けば不要なコードを避けられるけれど、注意力も必要。
CALL / RETURN では、もうちょっと微妙な問題が出ます。PCLATH / CALL で呼び出して、RETURNすると、無事に戻ってこられる(スタックには完全なアドレスがあるわけですね)。けれど、PCLATHが戻るわけではないので、不用意に近所のサブルーチンをPCLATHの操作無しに呼び出すと、吹っ飛んでしまいます。PCLATHは自力で戻さなきゃいけませんです。
これは新しく強化された命令。WREGの内容で表の位置をCALLするというもの。単純に、PCLATHを上位バイト、WREGを下位バイトとしたアドレスをコールします。素直に使うならば、テーブルの位置は256バイト単位の切れ目にします。
これも新しく強化された命令です。PC+1+Wreg に分岐します。PCは実行時には次の命令を示しているので PC+1 がベース。テーブル分岐が便利にできる命令です。
PCLATHを全く意識しなくてよいし、もちろん、256の切れ目なども考えなくてよいのが便利なところです。
なんとこれも強化された新しい命令。無条件ジャンプ(相対アドレス)ですが、PICにはありませんでした。オフセットは9ビットあるので、256ワード前後飛べます。 GOTO の代わりに BRA を使った方が精神衛生上、よろしいかと思います。
スタックが16段になり、読み書きができるようになりました。
STKPTR が現在のスタックトップのアドレスを保持し、 TOSH / TOSL で現在のスタックトップをアクセスします。
STKPTR は、CALL / CALLW 命令の実行、または、割り込みの発生で、+1 、つまり増加し、RETURN / RETLW / RETIE などで -1、すなわち、減少します。
STKPTRは、5ビットの値を持ちます。1ビット多いのですが、これはスタックが存在しない状態(10h〜1fh)を表現するために余分なビットがついているからです。
STKPTRのリセット直後の値は、1fh。スタックは空ですからこれでいいわけです。最初のCALL が行われると、スタックの00hに戻りアドレスが詰まれ、STKPTRの値は 1fh +1 → 00h となります。
スタックの段数は増えたとはいえ、16段しかないので、ローカル変数のスタックフレームのような使い方はできません。戻りアドレスの調整や、特殊な分岐などプログラムフローを触るときに便利でしょう。(これも、PICが苦手だった部分ですね)
あって当然だったような命令が、ようやく追加されました。opcodeを見ると、バイナリベースで互換になるように、新しい命令はうまく隙間を縫ってコードを割り当てています(大抵は以前、NOP扱いだった部分です)。
特に欠けていた演算命令が装備されたので、PIC独特の汚い(エレガント?)コードを書かなくてすみます。
この単純な命令がないために、数ステップの芸術的な固定処理を毎度書いていましたが、一命令で済みます。
今まで、ローテーション命令しかなかったので、少し工夫(といっても2命令で済んだ)しなければならなかった処理が一命令で書けるようになりました。こちらは、それほど貢献しないかな。
バンク切替(データメモリ)、ページ切替(プログラムメモリ)のために、それぞれ該当するレジスタに定数をセットする命令が用意されました。Wregを経由しないで直接セットできるところがミソです。
MOVLB は、BSRに定数をセット、つまり、バンク切り替え命令。
MOVLP は、PCLATHに定数をセット、つまりページ切り替え命令に相当します。
二つの命令共に、STATUSが変わらないのも当たり前だけど便利なところ。
PICの弱点ともいえるテーブル分岐、あるいはテーブル処理をシンプルに書く配慮。それと、当たり前の相対ジャンプ。これらだけでも、結構便利になったと言えますね。
また、テーブル処理は、FSRを使ってプログラムメモリーを読み出す方法でも実現できます。
大雑把に言えば Wreg による相対コール。飛び先は、上位:PCLATH と 下位:Wreg の組み合わせというシンプルなもの。PCLATHにテーブルベースアドレスを書き、Wregでインデックスして呼び出す。RETLWを並べておけばバイトデータのテーブル引き。GOTO あるいはBRAを並べておけばテーブルによるCALLとなります。
これは Wreg による分岐。飛び先が PC+1+W なので、W=0のときは、ちょうど次の命令。以下、順に並ぶ命令に Wreg をインデックスにして飛び込みます。
いろいろと使えますが、何より、PCLATHを触らなくてよいのが大きな長所。テーブルの位置を気にせずにプログラムが書けます。
単純な相対ジャンプ命令で、前後におよそ256ステップほど移動できます。これも、PCLATHの影響を受けないので便利。PICは命令効率が良いので、(というか、ソフトウェアの規模が小さいので?)前後に256ステップも移動できれば、サブルーチンの中のジャンプは、GOTOを使わずにBRAで殆どカバーできそうです。
間接アドレス操作に関する新設命令はわずか3命令ですが、結構、大きな貢献をします。
といっても、MOVIW と MOVWI はそれぞれ、実際は二つの別の命令に同じニモニックを割っているから、実際は5命令です。
FSRに定数を加算します。定数は符号付6ビット(-32〜+31)。オフセットの計算が簡単にできるところがミソ。Wregも壊れない(STATUSも変わらない)ので、スタックフレームの操作などに便利っぽいですね。
ADDFSR FSRn,k
と書きます
INDFnからデータを読むと、FSRnで示されたアドレスからデータを読みます。この命令を使ってこの読み出しを行うとき、FSRに対して PREINC,PREDEC,POSTINC,POSTDECが行われます。
処理 | 記述 | |
---|---|---|
PREINC | MOVIW ++FSn | FSRn に +1 してから読み出し |
PREDEC | MOVIW -FSRn | FSRn に -1 してから読み出し |
POSTINC | MOVIW FSRn++ | 読み出し後、 FSRn を +1 |
POSTDEC | MOVIW FSRn- | 読み出し後、 FSRn を -1 |
MOVIWはFSRnから相対オフセットを示して読み出せます。次のように書きます。
MOVIW k[FSRn]
オフセットは、符号付き6ビット(-32〜+31)で、定数です。
MOVIW とは逆に、Wregの内容をINDFnに書きます(FSRの示すところに書きます)。こちらも、FSRに対して PREINC,PREDEC,POSTINC,POSTDECが行われます。
処理 | 記述 | |
---|---|---|
PREINC | MOVWI ++FSn | FSRn に +1 してから書き込み |
PREDEC | MOVWI -FSRn | FSRn に -1 してから書き込み |
POSTINC | MOVWI FSRn++ | 書き込み、 FSRn を +1 |
POSTDEC | MOVWI FSRn- | 書き込み、 FSRn を -1 |
同様にオフセットつきで書けます。
MOVWI k[FSRn]
オフセットは、符号付き6ビット(-32〜+31)で、定数です。