FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

Chiselを使ってCPUを作ろう(6. パイプラインと分岐命令の実装)

Chiselを使って、非常にシンプルなCPUを作ってみるプロジェクト、ある程度演算命令は動き始めたが、ちゃんと動作しているのかどうかを確かめたい。

分岐命令はALUで比較を実行して、その結果に応じてPCを切り替える。 RISC-Vには3種類のPCアップデートの方法があって、

  • Branch Conditional式 : 比較の結果、PC+Immで分岐する。
  • J式 : 比較をしない。PC+Immで分岐する。
  • JALR : 比較をしない。レジスタ値で分岐する。

の大きく分けで3つで考えていればよい。Branchの場合は比較処理を実行して、そうでない場合は無条件でPCをアップデートする。 ChiselでもVerilogでも、書くことは一緒だ。

  val dec_jalr_en = u_cpath.io.ctl.jalr
  val dec_jal_en  = u_cpath.io.ctl.jal
  val dec_br_en   = u_cpath.io.ctl.br & (u_alu.io.res === 1.S)
  val dec_jump_en = if_inst_en & (dec_jalr_en | dec_jal_en | dec_br_en)

  when(if_inst_en & dec_jalr_en) {
    if_inst_addr := dec_reg_op0.asUInt
  } .elsewhen (if_inst_en & dec_jal_en) {
    if_inst_addr := dec_inst_addr + dec_imm_j
  } .elsewhen (if_inst_en & dec_br_en) {
    if_inst_addr := dec_inst_addr + dec_imm_b_sext
  } .elsewhen(if_inst_en & io.inst_ack) {
    if_inst_addr := if_inst_addr + 4.U
  }

今のところはパイプラインとしてはたったの2段。データメモリもくっついていないけど、とりあえず演算命令は動き始めている。

f:id:msyksphinz:20181121005036p:plain

ちなみにPCから命令を流しっぱなしだと、PCをアップデート後にすでに次のPCアドレスに移ってしまっているので、それを無効化する処理を入れている(1サイクルのバブル)。以下のコードで、`dec_inst_valid信号は、直前の命令がジャンプ命令ならば次の命令は無効化する。

  when  (if_inst_en & io.inst_ack) {
    dec_inst_data    := io.inst_data
    dec_inst_addr := if_inst_addr
    dec_inst_valid := Mux(dec_jump_en, false.B, true.B)
  } .otherwise {
    dec_inst_valid := false.B
  }

とりあえずこれで演算命令、分岐命令は動作するようになった。次はメモリアクセス命令、CSRの実装、あとはパイプラインをより深くしていく。