FPGA開発日記

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

Chiselを使ってCPUを作ろう(8. CSR命令の実装方法調査)

Chiselを使って、非常にシンプルなCPUを作ってみるプロジェクト、算術演算命令、ロードストア命令とくれば次はCSR命令だ。

f:id:msyksphinz:20181123005953p:plain

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_addrCSRのビット数分だけの長さの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