diff --git a/src/main/scala/chisel3/util/BlackBoxUtils.scala b/src/main/scala/chisel3/util/BlackBoxUtils.scala index 74d99ff8f33..21bd4dfab4b 100644 --- a/src/main/scala/chisel3/util/BlackBoxUtils.scala +++ b/src/main/scala/chisel3/util/BlackBoxUtils.scala @@ -32,6 +32,11 @@ trait HasBlackBoxResource extends BlackBox { trait HasBlackBoxInline extends BlackBox { self: BlackBox => + /** Creates a black box verilog file, from the contents of a local string + * + * @param blackBoxName The black box module name, to create filename + * @param blackBoxInline The black box contents + */ def setInline(blackBoxName: String, blackBoxInline: String): Unit = { val anno = new ChiselAnnotation with RunFirrtlTransform { def toFirrtl = BlackBoxInlineAnno(self.toNamed, blackBoxName, blackBoxInline) diff --git a/src/main/scala/chisel3/util/ExtModuleUtils.scala b/src/main/scala/chisel3/util/ExtModuleUtils.scala new file mode 100644 index 00000000000..831639be325 --- /dev/null +++ b/src/main/scala/chisel3/util/ExtModuleUtils.scala @@ -0,0 +1,61 @@ +// See LICENSE for license details. + +package chisel3.util + +import chisel3.experimental.{ChiselAnnotation, ExtModule, RunFirrtlTransform} +import firrtl.transforms.{BlackBoxPathAnno, BlackBoxResourceAnno, BlackBoxInlineAnno, BlackBoxSourceHelper} + +trait HasExtModuleResource extends ExtModule { + self: ExtModule => + + /** Copies a resource file to the target directory + * + * Resource files are located in project_root/src/main/resources/. + * Example of adding the resource file project_root/src/main/resources/blackbox.v: + * {{{ + * addResource("/blackbox.v") + * }}} + */ + def addResource(blackBoxResource: String): Unit = { + val anno = new ChiselAnnotation with RunFirrtlTransform { + def toFirrtl = BlackBoxResourceAnno(self.toNamed, blackBoxResource) + def transformClass = classOf[BlackBoxSourceHelper] + } + chisel3.experimental.annotate(anno) + } +} + +trait HasExtModuleInline extends ExtModule { + self: ExtModule => + + /** Creates a black box verilog file, from the contents of a local string + * + * @param blackBoxName The black box module name, to create filename + * @param blackBoxInline The black box contents + */ + def setInline(blackBoxName: String, blackBoxInline: String): Unit = { + val anno = new ChiselAnnotation with RunFirrtlTransform { + def toFirrtl = BlackBoxInlineAnno(self.toNamed, blackBoxName, blackBoxInline) + def transformClass = classOf[BlackBoxSourceHelper] + } + chisel3.experimental.annotate(anno) + } +} + +trait HasExtModulePath extends ExtModule { + self: ExtModule => + + /** Copies a file to the target directory + * + * This works with absolute and relative paths. Relative paths are relative + * to the current working directory, which is generally not the same as the + * target directory. + */ + def addPath(blackBoxPath: String): Unit = { + val anno = new ChiselAnnotation with RunFirrtlTransform { + def toFirrtl = BlackBoxPathAnno(self.toNamed, blackBoxPath) + def transformClass = classOf[BlackBoxSourceHelper] + } + chisel3.experimental.annotate(anno) + } +} diff --git a/src/test/scala/chiselTests/ExtModuleImpl.scala b/src/test/scala/chiselTests/ExtModuleImpl.scala new file mode 100644 index 00000000000..f71a1335817 --- /dev/null +++ b/src/test/scala/chiselTests/ExtModuleImpl.scala @@ -0,0 +1,141 @@ +// See LICENSE for license details. + +package chiselTests + +import java.io.File + +import chisel3._ +import chisel3.experimental.ExtModule +import chisel3.stage.{ChiselGeneratorAnnotation, ChiselStage} +import chisel3.util.{HasExtModuleInline, HasExtModulePath, HasExtModuleResource} +import firrtl.FirrtlExecutionSuccess +import firrtl.options.TargetDirAnnotation +import firrtl.stage.FirrtlCircuitAnnotation +import org.scalacheck.Test.Failed +import org.scalatest.{FreeSpec, Matchers, Succeeded} + +//scalastyle:off magic.number + +class ExtModuleAdd(n: Int) extends ExtModule with HasExtModuleInline { + val io = IO(new Bundle { + val in = Input(UInt(16.W)) + val out = Output(UInt(16.W)) + }) + + //scalastyle:off regex + setInline("ExtModuleAdd.v", s""" + |module ExtModuleAdd( + | input [15:0] in, + | output [15:0] out + |); + | assign out = in + $n; + |endmodule + """.stripMargin) +} + +class UsesExtModuleAddViaInline extends Module { + val io = IO(new Bundle { + val in = Input(UInt(16.W)) + val out = Output(UInt(16.W)) + }) + + val blackBoxAdd = Module(new ExtModuleAdd(5)) + blackBoxAdd.io.in := io.in + io.out := blackBoxAdd.io.out +} + +class ExtModuleMinus extends ExtModule with HasExtModuleResource { + val io = IO(new Bundle { + val in1 = Input(UInt(16.W)) + val in2 = Input(UInt(16.W)) + val out = Output(UInt(16.W)) + }) + addResource("/chisel3/BlackBoxTest.v") +} + +class ExtModuleMinusPath extends ExtModule with HasExtModulePath { + val io = IO(new Bundle { + val in1 = Input(UInt(16.W)) + val in2 = Input(UInt(16.W)) + val out = Output(UInt(16.W)) + }) + addPath( + new File("src/test/resources/chisel3/BlackBoxTest.v").getCanonicalPath + ) +} + +class UsesExtModuleMinusViaResource extends Module { + val io = IO(new Bundle { + val in1 = Input(UInt(16.W)) + val in2 = Input(UInt(16.W)) + val out = Output(UInt(16.W)) + }) + + val mod0 = Module(new ExtModuleMinus) + + mod0.io.in1 := io.in1 + mod0.io.in2 := io.in2 + io.out := mod0.io.out +} + +class UsesExtModuleMinusViaPath extends Module { + val io = IO(new Bundle { + val in1 = Input(UInt(16.W)) + val in2 = Input(UInt(16.W)) + val out = Output(UInt(16.W)) + }) + + val mod0 = Module(new ExtModuleMinusPath) + + mod0.io.in1 := io.in1 + mod0.io.in2 := io.in2 + io.out := mod0.io.out +} + +class ExtModuleImplSpec extends FreeSpec with Matchers { + "ExtModule can have verilator source implementation" - { + + "Implementations can be contained in-line" in { + val targetDir = "test_run_dir/extmodule-inline" + + val annotations = Seq( + TargetDirAnnotation(targetDir), + ChiselGeneratorAnnotation(() => new UsesExtModuleAddViaInline) + ) + val newAnnotations = (new ChiselStage).transform(annotations) + + newAnnotations.exists(_.isInstanceOf[FirrtlCircuitAnnotation]) should be (true) + val verilogOutput = new File(targetDir, "ExtModuleAdd.v") + verilogOutput.exists() should be(true) + verilogOutput.delete() + } + + "Implementations can be contained in resource files" in { + val targetDir = "test_run_dir/extmodule-resource" + val annotations = Seq( + TargetDirAnnotation(targetDir), + ChiselGeneratorAnnotation(() => new UsesExtModuleMinusViaResource) + ) + val newAnnotations = (new ChiselStage).transform(annotations) + + newAnnotations.exists(_.isInstanceOf[FirrtlCircuitAnnotation]) should be (true) + val verilogOutput = new File(targetDir, "BlackBoxTest.v") + verilogOutput.exists() should be(true) + verilogOutput.delete() + } + + "Implementations can be contained in arbitrary files" in { + val targetDir = "test_run_dir/extmodule-path" + val annotations = Seq( + TargetDirAnnotation(targetDir), + ChiselGeneratorAnnotation(() => new UsesExtModuleMinusViaPath) + ) + val newAnnotations = (new ChiselStage).transform(annotations) + + newAnnotations.exists(_.isInstanceOf[FirrtlCircuitAnnotation]) should be (true) + val verilogOutput = new File(targetDir, "BlackBoxTest.v") + verilogOutput.exists() should be(true) + verilogOutput.delete() + } + } +}