前回は少し手を抜いたような形で新しいデータ型を追加してみたが、少し真面目にデータ型の追加方法について検討してみる。
前回も書いたが、UInt
, SInt
, Bool
が定義されており、さらに良く調べてリムとexperimental
でFixedPoint
が定義されている。
これはBits
クラスをベースにして拡張されている。前回のTInt
型はUInt
を継承する形で定義したが、今回はまじめにBits
型を継承する形で追加してみる。
chiselFrontend/src/main/scala/chisel3/package.scala
TInt
型を便利使うための様々なルーチンを定義する。例えば、0.T(10.W)
で10ビットのTInt型を定義するなどが可能になる。
あとは、asTInt
などを定義して、他の型からTInt
型に変換できるようにしてみる。
diff --git a/chiselFrontend/src/main/scala/chisel3/package.scala b/chiselFrontend/src/main/scala/chisel3/package.scala index bd724369..1ebb3afb 100644 --- a/chiselFrontend/src/main/scala/chisel3/package.scala +++ b/chiselFrontend/src/main/scala/chisel3/package.scala @@ -37,12 +37,18 @@ package object chisel3 { // scalastyle:ignore package.object.name /** Int to UInt conversion, recommended style for constants. */ def U: UInt = UInt.Lit(bigint, Width()) // scalastyle:ignore method.name + /** Int to TInt conversion, recommended style for constants. + */ + def T: TInt = TInt.Lit(bigint, Width()) // scalastyle:ignore method.name /** Int to SInt conversion, recommended style for constants. */ def S: SInt = SInt.Lit(bigint, Width()) // scalastyle:ignore method.name /** Int to UInt conversion with specified width, recommended style for constants. */ def U(width: Width): UInt = UInt.Lit(bigint, width) // scalastyle:ignore method.name + /** Int to TInt conversion with specified width, recommended style for constants. + */ + def T(width: Width): TInt = TInt.Lit(bigint, width) // scalastyle:ignore method.name /** Int to SInt conversion with specified width, recommended style for constants. */ def S(width: Width): SInt = SInt.Lit(bigint, width) // scalastyle:ignore method.name @@ -50,12 +56,18 @@ package object chisel3 { // scalastyle:ignore package.object.name /** Int to UInt conversion, recommended style for variables. */ def asUInt(): UInt = UInt.Lit(bigint, Width()) + /** Int to TInt conversion, recommended style for variables. + */ + def asTInt(): TInt = TInt.Lit(bigint, Width()) /** Int to SInt conversion, recommended style for variables. */ def asSInt(): SInt = SInt.Lit(bigint, Width()) /** Int to UInt conversion with specified width, recommended style for variables. */ def asUInt(width: Width): UInt = UInt.Lit(bigint, width) + /** Int to TInt conversion with specified width, recommended style for variables. + */ + def asTInt(width: Width): TInt = TInt.Lit(bigint, width) /** Int to SInt conversion with specified width, recommended style for variables. */ def asSInt(width: Width): SInt = SInt.Lit(bigint, width)
次に、実体を定義してみる。実体の定義は、UInt
やSInt
と同様にすべての演算子の挙動を定義する必要がある。
実装が必要なものをリストアップしてみると、結構ある。ただし殆どのものはUInt
のものをコピーしてくればよい。
気を付けなければならないのは他の型との演算の場合など。下記の演算では、zexit()
でいったんSInt
に変換してから演算を適用してから、最後にTInt
に変更する。
final def * (that: UInt): TInt = macro SourceInfoTransform.thatArg /** @group SourceInfoTransformMacro */ def do_* (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): TInt = { val thatToSInt = that.zext() val result = binop(sourceInfo, SInt(this.width + thatToSInt.width), TimesOp, thatToSInt) result.tail(1).asTInt }
上記で出てきているmacro SourceInfoTransform.thatArg
だが、これはどうもマクロらしい?thatArg
節が定義されているコードを調べると定義が出てくる。
// Workaround for https://github.com/sbt/sbt/issues/3966 object SourceInfoTransform class SourceInfoTransform(val c: Context) extends AutoSourceTransform { ... def thatArg(that: c.Tree): c.Tree = { q"$thisObj.$doFuncTerm($that)($implicitSourceInfo, $implicitCompileOptions)" }
つまりここで自分のオブジェクトに対してdoFuncTerm
を呼び出している。doFuncTerm
は、
coreMacros/src/main/scala/chisel3/internal/sourceinfo/SourceInfoTransform.scala
abstract class AutoSourceTransform extends SourceInfoTransformMacro { import c.universe._ /** Returns the TermName of the transformed function, which is the applied function name with do_ * prepended. */ def doFuncTerm: TermName = { val funcName = c.macroApplication match { case q"$_.$funcName[..$_](...$_)" => funcName case _ => throw new Exception(s"Chisel Internal Error: Could not resolve function name from macro application: ${showCode(c.macroApplication)}") // scalastyle:ignore line.size.limit } TermName("do_" + funcName) } }
これはつまりdef *
をdef do_*
に変換する処理が行われている、と思う。
さらに、FIRRTLのEmitterを改造して、TInt
型がMatchするように変更する。
src/main/scala/chisel3/internal/firrtl/Emitter.scala
private def emitType(d: Data, clearDir: Boolean = false): String = d match { // scalastyle:ignore cyclomatic.complexity line.size.limit case d: Clock => "Clock" case _: AsyncReset => "AsyncReset" case _: ResetType => "Reset" case d: chisel3.core.EnumType => s"UInt${d.width}" case d: UInt => s"UInt${d.width}" case d: SInt => s"SInt${d.width}" case d: TInt => s"TInt${d.width}" ...
もう一つ、FIRRTLに渡すためにConverterのextractType
を追加する。FIRRTLにTInt
型を追加していないので、今回はとりあえずTInt
をUInt
に変換して扱う。
chiselFrontend/src/main/scala/chisel3/internal/firrtl/Converter.scala
def extractType(data: Data, clearDir: Boolean = false): fir.Type = data match { // scalastyle:ignore cyclomatic.complexity line.size.limit case _: Clock => fir.ClockType case _: AsyncReset => fir.AsyncResetType case _: ResetType => fir.ResetType case d: EnumType => fir.UIntType(convert(d.width)) case d: UInt => fir.UIntType(convert(d.width)) case d: TInt => fir.UIntType(convert(d.width)) // msyksphinz : Temporary ...
という訳で、上記のコードをコンパイルして使用してみる。
sbt publishLocal
コンパイルしたバイナリを使用してChiselのプログラムをコンパイルしてみる。
package tint_test import chisel3._ class tint_test extends Module { val io = IO(new Bundle { val in1 = Input(TInt(8.W)) val in2 = Input(TInt(8.W)) val out = Output(TInt(8.W)) }) io.out := io.in1 * io.in2 } object main extends App { chisel3.Driver.emitVerilog(new tint_test()) }
module tint_test( input clock, input reset, input [7:0] io_in1, input [7:0] io_in2, output [7:0] io_out ); wire [15:0] _T; // @[tint_test.scala 12:20] assign _T = io_in1 * io_in2; // @[tint_test.scala 12:20] assign io_out = _T[7:0]; // @[tint_test.scala 12:10] endmodule