Show page source of PicoBlazeBankedMemory #66377

= !PicoBlazeBankedMemory =

注:以下の方式はアイディア段階であり、実験はまだなので、'''まだ何の実績もありません'''。

PicoBlaze3(KCPSM3)の命令アドレスは10bitなので、命令数は1024が限界です。
Virtex6やSpartan6向けにはもっと広い命令空間を持つPicoBlaze6があるのですが、Spartan3系では使用できません。

命令数が1024を超えた場合、複数のサブルーチンの共通処理をくくりだしてJUMPで繋いだり、レジスタsXが0,1,2のどれかの判定をCOMPARE sX, 1だけでやったり(0ならC、1ならZ、2ならNCかつNZ)と、1命令単位でカリカリに削る努力をしなければなりません。

そんな努力をしなくても救われるために、バンクメモリ方式を適用してみることにしました。

= どう切り替えるか =
まず、PicoBlazeは、レジスタの値を命令アドレスとみなしてJUMPあるいはCALLするということができません。命令アドレスは10bit、データ幅は8bitなので、原理的に無理です。よって、s4にバンク番号、s5にサブルーチンのアドレスを入れて、トランポリンルーチンでs4の値にバンク切り替えしたあとにs5の値をアドレスとしてCALL、なんていうのは不可能です。

Banked Memoryの普通の方式は、メモリ空間を4ブロックぐらいに分割し、そのうち何ブロックかを窓として、窓の先に見える実メモリをとっかえひっかえするものです。

バンク切り替えは、切り替えの対象でないブロックにバンク切り替えルーチン(仮に、トランポリンルーチンと表記します)をおいて、トランポリンルーチンに「バンクを切り替え、目的のルーチンをCALLし、戻ってきたら切り替え前のバンクに戻してリターン」という仕事をさせるのがセオリーです。

トランポリンルーチンに頼らずに、切り替えの対象であるブロックを実行中にバンク切り替えをしてしまうと、自分の足場がなくなってしまうので普通はやりません。

普通はやらないのですが、PicoBlazeだと一番合理的な実装になる予感がしたので、やってみることにしました。

また、命令メモリ空間を分割するとただでさえ狭い空間がさらに狭くなるし、pBlazIDEでシミュレーションするのも困難になるので、
命令メモリ空間の分割はせずにメモリを丸ごと入れ替えてしまうことにしました。それでもデータメモリ(ScratchPad)は同一なので困らないはずです。

※以下の記述でnopと書いてある部分は、PicoBlazeにはnopが無いので実際にはload s0, s0です。また、ラベル名の最後に_がついているルーチンは、本体が別のバンクにあることを示しています。


bankA, bankB共通
{{{ code asm
bankA_op      DSOUT $80
bankB_op      DSOUT $81
bankPREV_op   DSOUT $40
}}}
bankA用
{{{ code asm
                  適当なプログラム
                  call LCDinit_
                  ...
                  call LCDput_
                  ...
    isr:          割り込みサービスルーチン
                  ...
                  ret
                  ORG $3F3
3F3 LCDinit_:     call bankchg_to_B
3F4               nop
3F5 LCDput_:      call bankchg_to_B
3F6               nop
3F7               nop
3F8 bankchg_to_B: out s0, bankB_op
3F9               ret
3FA bankchg_prev: out s0, bankPREV_op
3FB               ret
3FC isr_:         nop
3FD               call isr
3FE               jump bankchg_prev
3FF isv:          jump isr
}}}

bankB用
{{{ code asm
    LCDinit:      LCD初期化処理
                  ...
                  ret
    LCDput:       LCDに文字を出す処理
                  ...
                  ret
                  ORG $3F3
3F3 LCDinit_:     nop
3F4               call LCDinit
3F5 LCDput_:      jump bankchg_prev
3F6               call LCDput
3F7               jump bankchg_prev
3F8 bankchg_to_A: out s0, bankA_op
3F9               ret
3FA bankchg_prev: out s0, bankPREV_op
3FB               ret
3FC isr_:         call bankchg_to_A
3FD               nop
3FE               nop
3FF isv:          jump isr_
}}}

上記のようなプログラムと、「ポートアドレス$80または$81に書き込みがあったら、現在の選択バンクをスタック構造へpushし、$80ならbankAへ、$81ならbankBにバンク切り替えする」かつ「ポートアドレス$40に書き込みがあったら、スタック構造からpopした値にバンク切り替えする」モジュールをハードウェアとしてPicoBlazeに接続します。

※ちなみに、上記のモジュールはPicoBlazeのOUT_PORTを無視するので、s0の値はどうでもよいです。

bankA上のプログラムから、bankB上のLCD_initを呼び出す場合、その流れは以下のようになります。

||  || 実行される命令            || 命令の場所||
|| 1|| {{{call LCDinit_}}}       || bankA||
|| 2|| {{{call bankchg_to_B}}}   || bankA 3F3||
|| 3|| {{{out s0, bankB_op}}}    || bankA 3F8||
|| 4|| {{{ret}}}                 || bankA 3F9||
|| 5|| {{{call LCDinit}}}        || bankB 3F4(この時点でbankBが選択されている)||
|| 6|| {{{LCDinitの処理が行われる}}}||bankB||
|| 7|| {{{ret}}}||bankB||
|| 8|| {{{jump bankchg_prev}}}   || bankB 3F5||
|| 9|| {{{out s0, bankPREV_op}}} || bankB 3FA||
||10|| {{{ret}}}                 || bankB 3FB||
||11|| {{{bankAの、call LCDinit_の次の命令}}}|| bankA ||

bankBが選択されているときに割り込みが発生した場合は、4命令ぶんのオーバヘッドがあるもののbankAに存在するisrが呼び出され、isrでの処理が終わり次第bankBが再選択され戻ってくるので問題なしです。

...PicoBlazeで開発していると、こうやってソフトとハードを両方設計して、連係動作させて問題を解決するということできるのでとても楽しいです。
新人にSpartan3A Starter Kit(!DigiKeyで2万ぐらいで購入できます)を渡して、「PicoBlazeを使ってナイトライダーを作れ」とかいう課題を与えれば短期間で両刀使いが育成できるかもしれませんね。