Chiselを使って、非常にシンプルなCPUを作ってみるプロジェクト、算術演算命令、ロードストア命令とくれば次はCSR命令だ。
RISC-VにはCSR(Control and Status Register)が多くの数定義されている。 これらをChiselで記述するためにはどうしたらよいのだろうか。 まずは比較的読みやすいRISC-VのChisel実装、Sodorを読んでみる。
https://github.com/ucb-bar/riscv-sodor/blob/master/src/common/csr.scala
val read_mapping = collection.mutable.LinkedHashMap[Int,Bits]( /* CsrAddr.mcycle -> reg_time, */ /* CsrAddr.minstret -> reg_instret, */ CsrAddr.mimpid -> 0.U, CsrAddr.marchid -> 0.U, CsrAddr.mvendorid -> 0.U, CsrAddr.misa -> misa.U, CsrAddr.mimpid -> impid.U, ...
ScalaにはLinkedHashMap
という関数があるらしい。
名前から容易に想像できるが、Hashみたいなものだ。Intの値に対してBitsの値をMapするための関数。
上記のCsrAddr.misa
(= 0x301)に対してmisa.U
をマップしている。これをCSRレジスタ分くっつける訳だ。
CSRをReadする場合には、Mappingで取り出す。
以下の記述は良く分からないけれども、decoded_addr
はCSRのビット数分だけの長さの1-hot信号になっているのだと思う。そこから読み込みデータを選択する。
val decoded_addr = read_mapping map { case (k, v) => k -> (io.rw.addr === k.U(12.W)) } io.rw.rdata := Mux1H(for ((k, v) <- read_mapping) yield decoded_addr(k) -> v)
データのアップデート(Set / Clear)の場合は、以下の論理を通すらしい。難しいなあ。
def readModifyWriteCSR(cmd: UInt, rdata: UInt, wdata: UInt) = (Mux((cmd === CSR.Set | cmd === CSR.Clear), rdata, 0.U) | wdata) & ~Mux(cmd === CSR.Clear, wdata, 0.U)
砕いていくと、 cmd === CSR.Set
の場合以下のようになる。これで確かにwdataのうち1が立っている部分は必ずセットされる。
def readModifyWriteCSR(CSR.Set, rdata: UInt, wdata: UInt) = (rdata | wdata) & 0xffffffff
cmd == CSR.Clear
の場合以下のようになる。これで確かにrddataのうち1が立っている部分は必ずクリアされる。
def readModifyWriteCSR(CSR.Clear, rdata: UInt, wdata: UInt) = (rdata | wdata) & ~wdata