FPGA開発日記

FPGAというより、コンピュータアーキテクチャかもね! カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages

Chiselでtypedef(のようなもの)を実現する方法

https://cdn-ak.f.st-hatena.com/images/fotolife/m/msyksphinz/20170830/20170830023047.png

ハードウェア記述言語ChiselはScalaをベースにした言語であり、Scalaの言語単位をベースにして設計してある。

それでは、ハードウェアの配線や変数を表すためにどのような型が用意してあるかというと、例えば、以下のような型が用意されている。

  • UInt : 任意の幅を定義できる符号なしの整数を格納するための型
  • SInt : 任意の幅を定義できる符号ありの整数を格納するための型
  • Bool : 1ビットのTrue/Falseを格納するための型

例えば、配線とレジスタを定義するためには以下のようにして宣言する。WireRegというクラスに型の情報を与え、実体化させるという訳だ。

val w_a = Wire(UInt(32.W))
val r_a = Reg(Bool())

ところで、Chiselの型の中で主に使うのは上記の3つくらいしかない。しかし、System Verilogにはtypedefにより様々な型を定義できる。 ハードウェアの設計に当たり、どの信号がどの型を持っているのか正確に区別できた方が設計がはかどりやすい。

そこで、Chiselの型をさらに増やすにはどのようにすればよいか、つまりtypedefのようなことを実現するためにはどうすればよいか、調査した。

ScalaにはtypeというTypedefに似たような機能がある。

  type new_type = UInt

例えば、上記のnew_typeを使って以下の3つの値を宣言する。

  type new_type = UInt
  val ADD: new_type = 0.U(2.W)
  val SUB: new_type = 1.U(2.W)
  val AND: new_type = 2.U(2.W)

しかしこれだけではだめで、Chiselで実際に活用するためにはObjectも宣言する。この時、UIntを定義するために使用しているUIntFactoryを継承して使用する。 そしてnew_typeを呼び出すときに型の幅も固定してしまう。例えば、下記だとnew_typeは2ビットに固定される。

  type new_type = UInt
  val ADD: new_type = 0.U(2.W)
  val SUB: new_type = 1.U(2.W)
  val AND: new_type = 2.U(2.W)
  object new_type extends UIntFactory {
    override def apply(): UInt = apply(2.W)
  }

これで型が作れた。さっそく使ってみる。

import pkg._

class typedef_test extends Module
{
  val io = IO(new Bundle {
    val op  = Input(new_type())
    val in0 = Input(UInt(32.W))
    val in1 = Input(UInt(32.W))
    val out = Output(UInt(32.W))
  })

  io.out := 0.U(32.W)
  switch(io.op) {
    is(ADD) { io.out := io.in0 + io.in1 }
    is(SUB) { io.out := io.in0 - io.in1 }
    is(AND) { io.out := io.in0 & io.in1 }
  }
}

入力信号opの型をnew_typeに設定した。実体はUInt(2.W)だが、これだけで少し見栄えが良くなる。