ハードウェア記述言語ChiselはScalaをベースにした言語であり、Scalaの言語単位をベースにして設計してある。
それでは、ハードウェアの配線や変数を表すためにどのような型が用意してあるかというと、例えば、以下のような型が用意されている。
- UInt : 任意の幅を定義できる符号なしの整数を格納するための型
- SInt : 任意の幅を定義できる符号ありの整数を格納するための型
- Bool : 1ビットのTrue/Falseを格納するための型
例えば、配線とレジスタを定義するためには以下のようにして宣言する。Wire
やReg
というクラスに型の情報を与え、実体化させるという訳だ。
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)だが、これだけで少し見栄えが良くなる。