Chiselのコンパイルフローの解析の続き。前の記事は以下。
どこでChiselからFIRへの変換が行われているのかというと、それはChiselStage
のEmitter
が変換処理を行っているらしい。
chisel3/src/main/scala/chisel3/internal/firrtl/Emitter.scala
private def emit(e: Command, ctx: Component): String = { // scalastyle:ignore cyclomatic.complexity val firrtlLine = e match { case e: DefPrim[_] => s"node ${e.name} = ${e.op.name}(${e.args.map(_.fullName(ctx)).mkString(", ")})" case e: DefWire => s"wire ${e.name} : ${emitType(e.id)}" case e: DefReg => s"reg ${e.name} : ${emitType(e.id)}, ${e.clock.fullName(ctx)}" case e: DefRegInit => s"reg ${e.name} : ${emitType(e.id)}, ${e.clock.fullName(ctx)} with : (reset => (${e.reset.fullName(ctx)}, ${e.init.fullName(ctx)}))" // scalastyle:ignore line.size.limit case e: DefMemory => s"cmem ${e.name} : ${emitType(e.t)}[${e.size}]" case e: DefSeqMemory => s"smem ${e.name} : ${emitType(e.t)}[${e.size}]" case e: DefMemPort[_] => s"${e.dir} mport ${e.name} = ${e.source.fullName(ctx)}[${e.index.fullName(ctx)}], ${e.clock.fullName(ctx)}" // scalastyle:ignore line.size.limit case e: Connect => s"${e.loc.fullName(ctx)} <= ${e.exp.fullName(ctx)}" case e: BulkConnect => s"${e.loc1.fullName(ctx)} <- ${e.loc2.fullName(ctx)}" case e: Attach => e.locs.map(_.fullName(ctx)).mkString("attach (", ", ", ")") case e: Stop => s"stop(${e.clock.fullName(ctx)}, UInt<1>(1), ${e.ret})" ...
ステップを突き詰めていくと、まずChisel3のemitVerilog
が呼び出されると、各種コンパイルのステージを通っていきChiselStage
というフェーズに入る。この中でEmitter
が呼び出され、FIRへの変換が行われるらしい。
ここでDefPrim
やらDefMemory
やらで挙動が変わるような仕組みになっているが、これはどこで宣言されているのだろうか?
Chiselでは、例えばレジスタを宣言する場合にDefRegInit
などのオブジェクトが宣言され、これによりオブジェクトの階層が構成されるようになっている。
chisel3/chiselFrontend/src/main/scala/chisel3/Reg.scala
object RegInit { /** Construct a [[Reg]] from a type template initialized to the specified value on reset * @param t The type template used to construct this [[Reg]] * @param init The value the [[Reg]] is initialized to on reset */ def apply[T <: Data](t: T, init: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = { if (compileOptions.declaredTypeMustBeUnbound) { requireIsChiselType(t, "reg type") } val reg = t.cloneTypeFull val clock = Builder.forcedClock val reset = Builder.forcedReset reg.bind(RegBinding(Builder.forcedUserModule)) requireIsHardware(init, "reg initializer") pushCommand(DefRegInit(sourceInfo, reg, clock.ref, reset.ref, init.ref)) reg } ...
上記の場合は、DefRegInit
というコマンドを登録している。DefRegInit
の宣言は以下だ。
chisel3/chiselFrontend/src/main/scala/chisel3/internal/firrtl/IR.scala
case class DefRegInit(sourceInfo: SourceInfo, id: Data, clock: Arg, reset: Arg, init: Arg) extends Definition
Module
の場合は、DefInstance
が呼び出される。
chisel3/chiselFrontend/src/main/scala/chisel3/Module.scala
object Module extends SourceInfoDoc { /** A wrapper method that all Module instantiations must be wrapped in * (necessary to help Chisel track internal state). ... */ /** @group SourceInfoTransformMacro */ def do_apply[T <: BaseModule](bc: => T) (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = { ... // Handle connections at enclosing scope if(!Builder.currentModule.isEmpty) { pushCommand(DefInstance(sourceInfo, module, component.ports)) module.initializeInParent(compileOptions) } module }