FPGA開発日記

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

Chiselを使ってCPUを作ろう(17. 5ステージにCPUにフォワーディングパスを追加する)

f:id:msyksphinz:20181220010907p:plain

Chiselを使って、非常にシンプルなCPUを作ってみるプロジェクト。5ステージまで拡張したので、次はフォワーディングパスを追加して正常に演算を実行できるようにする。

github.com

ALUのフォワーディングパスは、

  1. WBステージ → EXステージ
  2. MEMステージ → EXステージ
  3. EXステージ → EXステージ

の3種類のフォワードパスを追加して、書き込み先アドレスと使用アドレスが同一ならば、フォワードするようにする。 Verilogと書き方は全く同じだ。

  • minicpu/src/main/scala/cpu/cpu.scala
  switch(ex_op0_sel) {
    is (OP0_RS1) {
      // Register Forwarding
      when (mem_inst_valid & mem_inst_wb_en & (mem_inst_rd === ex_inst_rs0)) {
        ex_alu_op0 := mem_alu_res
      } .elsewhen (wb_inst_valid & wb_inst_wb_en & (wb_inst_rd === ex_inst_rs0)) {
        ex_alu_op0 := Mux (wb_ctrl_mem_cmd === MCMD_RD, wb_mem_rdval, wb_alu_res)
      } .otherwise {
        ex_alu_op0 := ex_rdata_op0
      }
...
  switch(ex_op1_sel) {
    // Register Forwarding
    is (OP1_PC ) { ex_alu_op1 := ex_inst_addr.asSInt }
    is (OP1_RS2) {
      when (mem_inst_valid & mem_inst_wb_en & (mem_inst_rd === ex_inst_rs1)) {
        ex_alu_op1 := mem_alu_res
      } .elsewhen (wb_inst_valid & wb_inst_wb_en & (wb_inst_rd === ex_inst_rs1)) {
        ex_alu_op1 := Mux (wb_ctrl_mem_cmd === MCMD_RD, wb_mem_rdval, wb_alu_res)
      } .elsewhen (ex_csr_wbcsr =/= CSR.X) {
        ex_alu_op1 := u_csrfile.io.rw.rdata.asSInt
      } .otherwise {
        ex_alu_op1 := ex_rdata_op1
      }
...

次に、ロードユースのフォワーディングだ。残念ながら、ロードした命令を次サイクルで次の命令が利用することはできない。メモリにアクセスするので、このフォワードは無効なのだ。 そこで、メモリアクセスが入ると、自動的に1サイクルストールさせるようにする。

  • minicpu/src/main/scala/cpu/cpu.scala
  val dec_stall_en  = dec_inst_valid & (u_cpath.io.ctl.mem_cmd =/= MCMD_X)
        49 :   [00000108]           | 1,X[ 1]=>0000000000002000,X[00]=>0000000000000000|                          |                               |X01<=0x0000000000002000|x01<=0x00000000000020fc  : 0x000000fc : INST(0x00002097) : auipc   ra, 0x2
        50 :   [0000010c]  00ff0eb7 |                                                  |                          | [00002000]=>0x0000000000000000|X30<=0x0000000000002000|x01<=0x0000000000002000  : 0x00000100 : INST(0xf0408093) : addi    ra, ra, -252
        51 :   [00000110]  0ffe8e9b |15,X[ 0]=>0000000000ff0000,X[15]=>0000000000000000|                          |                               |                       |x30<=0x0000000000ff00ff  : 0x00000104 : INST(0x0000af03) : lw      t5, 0(ra)
        52 :   [00000114]  00200193 |16,X[29]=>0000000000ff0000,X[00]=>00000000000000ff|                          |                               |X29<=0x0000000000ff0000|
        53 :   [00000118]  27df1a63 | 1,X[ 0]=>0000000000000000,X[00]=>0000000000000002|                          |                               |X29<=0x0000000000ff00ff|x29<=0x0000000000ff0000  : 0x00000108 : INST(0x00ff0eb7) : lui     t4, 0xff0
        54 :   [0000011c]  00002097 |11,X[30]=>0000000000ff00ff,X[29]=>0000000000ff00ff|                          |                               |X03<=0x0000000000000002|x29<=0x0000000000ff00ff  : 0x0000010c : INST(0x0ffe8e9b) : addiw   t4, t4, 255

これらのフォワーディングパスを追加することにより、無事にrv64ui_p_***のパタンがすべてPassするようになった。

Chiselを使った場合、Verilogを使った場合でどちらとも無事にPassしたので、たぶん大丈夫。