• Showing Page History #73181

PicoBlazeBankedMemory

r203 で、Spartan3E Starter Kitを使用した動作確認ができるようになりました。

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 $43
bankA用
  1. 適当なプログラム
  2. CALL init_lcd
  3. ...
  4. CALL write_lcdram
  5. ...
  6. isr: 割り込みサービスルーチン
  7. ...
  8. RET
  9. ORG $3F9
  10. 3F9 init_lcd: OUT s0, BANKB_OP
  11. 3FA RET
  12. 3FB write_lcdram: OUT s0, BANKB_OP
  13. 3FC RET
  14. 3FD nop
  15. 3FE RET
  16. 3FF isv: JUMP isr

bankB用

  1. init_lcd: LCD初期化処理
  2. ...
  3. RET
  4. write_lcdram: LCDに文字を出す処理
  5. ...
  6. RET
  7. ORG $3F9
  8. 3F9 init_lcd_: nop
  9. 3FA CALL init_lcd
  10. 3FB write_lcdram_:OUT s0, BANKPREV_OP
  11. 3FC CALL write_lcdram
  12. 3FD OUT s0, BANKPREV_OP
  13. 3FE nop
  14. 3FF nop

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

※ちなみに、上記のモジュールはPicoBlazeのOUT_PORTを無視するので、s0の値は動作に影響しません。

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

実行される命令 命令の場所
1 CALL init_lcd bankA
2 OUT s0, BANKA_OP bankA 3F9
3 CALL init_lcd bankB 3FA(この時点でbankBが選択されている)
4 init_lcdの処理が行われるbankB
5 RET bankB
6 OUT s0, BANKPREV_OP bankB 3FB
7 RET bankA 3FC(この時点でbankAが選択されている)
8 bankAの、CALL init_lcdの次の命令 bankA

割り込みに関しては、PBLAZBLOCK.vhdにて、「INTERRUPT_ACKがHighになったら強制的にBankAを選択し、RETI命令が検知されたら強制的にもとのバンクに戻す」という処理をしているため、オーバヘッドはありません。

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