RISC-Vを目指す(2)

RISC-V命令セットにしてみたらハンドアセンブルで命令を並べるのが大変になってしまったので、非常に雑にアセンブラを作ってみた。これもずっと前に作ったx86アセンブラを持ってきて書き直したものである。RISC-Vアセンブラ · GitHub
このような感じにシミュレータのコードに埋め込んで使う。

# 8アドレスぶんの命令
rom.d = RISCVASM.asm do
  addi r1, r0, 0
  addi r2, r0, 0
  addi r3, r0, 1

label :fibonacci
  add  r1, r0, r2
  add  r2, r0, r3
  add  r3, r1, r2
  jal  r0, :fibonacci
  nop
end.map{|t|_to_pin(t, vcc, gnd)}.reverse

jal命令追加

jalrはレジスタ間接絶対アドレスジャンプなのだと思うのだが、レジスタ間接のためにアセンブラでラベル指定ができない。できないわけじゃないけどちょっと微妙なので、相対ジャンプであるjal命令を実装してみた。相対ジャンプはPCの値と即値を加算してジャンプ先アドレスを求める必要があり、adderの入力パターンが増えてややこしくなる。

# 32bit全加算器
adder.d = [_if(insn_jal, pc.o, regf.o0), _if(insn_jal, [insn_bit_imm20_1[0]]*11 + insn_bit_imm20_1 + [gnd], # jal命令
                                         _if(insn_add, regf.o1, # add命令
                                                       [insn_bit_imm11_0[0]]*20 + insn_bit_imm11_0 # それ以外
                                         ))
          ]
adder.x = gnd

このへんはもうちょっと書きやすくする仕掛けが欲しいところである。また、jal命令の即値は順番が単純じゃなく、これまた非常にややこしい。

insn_bit_imm20_1 = [insn[0]] + insn[12..19] + [insn[11]] + insn[1..10]

なんでこうなっているのかというと、他の命令の配置となるべく同じ位置に同じ線を配置するためであり、例えば即値の[10:5]はI、S、B、Jの4タイプで共通の位置となっている。このおかげで命令によってピンの意味が変わることが少なく、マルチプレクサを置く必要が無い、という話だ。
jal命令の即値は20bitだが、実際には21〜1の範囲が命令内にエンコードされている。最下位のビットは常に0(偶数アドレスのみ)で、4の倍数にしていないのは16bit命令セットを扱うことを考えているからかもしれない。最上位bitを符号として扱うことで±1MBの相対ジャンプができる。
RISC-Vの命令の即値は基本的に全て符号付である。addiは即値を足す命令だが、subiは無い。2の補数と足してくれ、と。符号bitは常に命令の最上位に位置するように作られている。随所に回路が簡単になる工夫が詰め込まれているのだが、俺はいまのところそれを有効に活用できている気がしない。

おしまい

最初のフィボナッチ数列計算のコードを実行するとこうなる。

コードはこちら
アセンブラができたのでもうちょい規模の大きなコードが書けそうである。そうなってくるとスタックとか欲しくなるし、条件分岐命令も欲しい。俺としてはテキストVRAMを用意してDXRuby側で拾って文字を描画したいのだが。はてさて。