• Showing Page History #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共通

  1. bankA_op DSOUT $80
  2. bankB_op DSOUT $81
  3. bankPREV_op DSOUT $40
bankA用
  1. 適当なプログラム
  2. call LCDinit_
  3. ...
  4. call LCDput_
  5. ...
  6. isr: 割り込みサービスルーチン
  7. ...
  8. ret
  9. ORG $3F3
  10. 3F3 LCDinit_: call bankchg_to_B
  11. 3F4 nop
  12. 3F5 LCDput_: call bankchg_to_B
  13. 3F6 nop
  14. 3F7 nop
  15. 3F8 bankchg_to_B: out s0, bankB_op
  16. 3F9 ret
  17. 3FA bankchg_prev: out s0, bankPREV_op
  18. 3FB ret
  19. 3FC isr_: nop
  20. 3FD call isr
  21. 3FE jump bankchg_prev
  22. 3FF isv: jump isr

bankB用

  1. LCDinit: LCD初期化処理
  2. ...
  3. ret
  4. LCDput: LCDに文字を出す処理
  5. ...
  6. ret
  7. ORG $3F3
  8. 3F3 LCDinit_: nop
  9. 3F4 call LCDinit
  10. 3F5 LCDput_: jump bankchg_prev
  11. 3F6 call LCDput
  12. 3F7 jump bankchg_prev
  13. 3F8 bankchg_to_A: out s0, bankA_op
  14. 3F9 ret
  15. 3FA bankchg_prev: out s0, bankPREV_op
  16. 3FB ret
  17. 3FC isr_: call bankchg_to_A
  18. 3FD nop
  19. 3FE nop
  20. 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 retbankB
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を使ってナイトライダーを作れ」とかいう課題を与えれば短期間で両刀使いが育成できるかもしれませんね。