Chiselを使って、非常にシンプルなCPUを作ってみるプロジェクト。折角なので5ステージまで拡張して、テストパタンが通るようにしたい。
参考にしたのはヘネパタの後ろの方に乗っている5ステージのプロセッサの解説。とりあえず図に乗っていることをすべてChiselで書き下してみることにした。 そしてテストパタンを動かし、間違っているところを直していく。
今のところChiselの書き方としては非常にイマイチだ。ただしVerilogに直さずにChiselのままシミュレーションをしているので、リグレッションなども非常に高速で助かる。
波形を見ずに開発をしているので、以下のようなパイプライントレースを出力できるようにしてデバッグを行えるようにした。波形を見ない、完全にログだけを見てデバッグできるか、試している。
cycle : IF | EX_STAGE_REG_READ | MEM_STAGE_MEM_ACCESS | WB_STAGE | 0 : | | | | 1 : [00000000] | | | | 2 : [00000004] 04c0006f | | | | 3 : 34202f73 | | | | 4 : [0000004c] | | |X00<=0x0000000000000000| 5 : [00000050] -ebfda8d | |x00<=0x0000000000000004 | | : 0x00000000 : INST(0x04c0006f) : j pc + 0x4c 6 : [00000054] 00051063 |21,X[ 0]=>0000000000000000,X[20]=>0000000000000000| | | 7 : [00000058] 00000297 |11,X[10]=>0000000000000000,X[00]=>0000000000000000| |X10<=0x0000000000000000| 8 : [0000005c] 01028293 | 1,X[ 0]=>0000000000000000,X[00]=>0000000000000054|x10<=0x0000000000000000 | | : 0x0000004c : INST(0xf1402573) : csrr a0, mhartid 9 : [00000060] 30529073 | 1,X[ 5]=>0000000000000054,X[00]=>0000000000000010| |X05<=0x0000000000000054| : 0x00000050 : INST(0x00051063) : bnez a0, pc + 0 10 : [00000064] 18005073 |21,X[ 5]=>0000000000000064,X[05]=>0000000000000064|x05<=0x0000000000000054 |X05<=0x0000000000000064| : 0x00000054 : INST(0x00000297) : auipc t0, 0x0 11 : [00000068] 00000297 |21,X[ 0]=>0000000000000000,X[00]=>0000000000000064|x05<=0x0000000000000064 |X00<=0x0000000000000064| : 0x00000058 : INST(0x01028293) : addi t0, t0, 16 12 : [0000006c] 01c28293 | 1,X[ 0]=>0000000000000000,X[00]=>0000000000000064|x00<=0x0000000000000064 |X00<=0x0000000000000064| : 0x0000005c : INST(0x30529073) : csrw mtvec, t0 13 : [00000070] 30529073 | 1,X[ 5]=>0000000000000064,X[00]=>000000000000001c|x00<=0x0000000000000064 |X05<=0x0000000000000064| : 0x00000060 : INST(0x18005073) : csrwi satp, 0 14 : [00000074] -00ffd6d |21,X[ 5]=>0000000000000080,X[05]=>0000000000000080|x05<=0x0000000000000064 |X05<=0x0000000000000080| : 0x00000064 : INST(0x00000297) : auipc t0, 0x0 15 : [00000078] 3b029073 | 1,X[ 0]=>0000000000000080,X[00]=>ffffffffffffffff|x05<=0x0000000000000080 |X00<=0x0000000000000080| : 0x00000068 : INST(0x01c28293) : addi t0, t0, 28 16 : [0000007c] 01f00293 |21,X[ 5]=>000000000000007f,X[16]=>0000000000000000|x00<=0x0000000000000080 |X05<=0x000000000000007f| : 0x0000006c : INST(0x30529073) : csrw mtvec, t0 17 : [00000080] 3a029073 | 1,X[ 0]=>0000000000000000,X[00]=>000000000000001f|x05<=0x000000000000007f |X00<=0x0000000000000000| : 0x00000070 : INST(0xfff00293) : li t0, -1 18 : [00000084] 00000297 |21,X[ 5]=>000000000000001f,X[00]=>0000000000000000|x00<=0x0000000000000000 |X05<=0x000000000000001f| : 0x00000074 : INST(0x3b029073) : csrw pmpaddr0, t0 19 : [00000088] 01828293 | 1,X[ 0]=>0000000000000000,X[00]=>0000000000000080|x05<=0x000000000000001f |X00<=0x0000000000000000| : 0x00000078 : INST(0x01f00293) : li t0, 31 20 : [0000008c] 30529073 | 1,X[ 5]=>0000000000000080,X[00]=>0000000000000018|x00<=0x0000000000000000 |X05<=0x0000000000000080| : 0x0000007c : INST(0x3a029073) : csrw pmpcfg0, t0
まだ分岐命令の処理で足りていない部分や、パイプラインが正しく切れていない場所があるが、少しずつ動き始めた。
フォワーディングの部分なども、一応実装している。Verilogと書き方はほとんど一緒だ。
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 := wb_alu_res
} .otherwise {
ex_alu_op0 := ex_reg_op0
}
}
...