diff options
| -rw-r--r-- | chiselFrontend/src/main/scala/chisel3/core/BlackBox.scala | 18 | ||||
| -rw-r--r-- | chiselFrontend/src/main/scala/chisel3/core/Module.scala | 10 | ||||
| -rw-r--r-- | chiselFrontend/src/main/scala/chisel3/internal/Builder.scala | 5 | ||||
| -rw-r--r-- | chiselFrontend/src/main/scala/chisel3/internal/firrtl/IR.scala | 8 | ||||
| -rw-r--r-- | src/main/scala/chisel3/internal/firrtl/Emitter.scala | 24 | ||||
| -rw-r--r-- | src/main/scala/chisel3/package.scala | 24 | ||||
| -rw-r--r-- | src/test/resources/BlackBoxTest.v | 25 | ||||
| -rw-r--r-- | src/test/scala/chiselTests/BlackBox.scala | 49 |
8 files changed, 140 insertions, 23 deletions
diff --git a/chiselFrontend/src/main/scala/chisel3/core/BlackBox.scala b/chiselFrontend/src/main/scala/chisel3/core/BlackBox.scala index 7fe429fa..85a57111 100644 --- a/chiselFrontend/src/main/scala/chisel3/core/BlackBox.scala +++ b/chiselFrontend/src/main/scala/chisel3/core/BlackBox.scala @@ -3,11 +3,20 @@ package chisel3.core import chisel3.internal.Builder.pushCommand -import chisel3.internal.firrtl.{ModuleIO, DefInvalid} +import chisel3.internal.firrtl._ +import chisel3.internal.throwException import chisel3.internal.sourceinfo.SourceInfo // TODO: remove this once we have CompileOptions threaded through the macro system. import chisel3.core.ExplicitCompileOptions.NotStrict +/** Parameters for BlackBoxes */ +sealed abstract class Param +case class IntParam(value: BigInt) extends Param +case class DoubleParam(value: Double) extends Param +case class StringParam(value: String) extends Param +/** Unquoted String */ +case class RawParam(value: String) extends Param + /** Defines a black box, which is a module that can be referenced from within * Chisel, but is not defined in the emitted Verilog. Useful for connecting * to RTL modules defined outside Chisel. @@ -16,12 +25,9 @@ import chisel3.core.ExplicitCompileOptions.NotStrict * {{{ * ... to be written once a spec is finalized ... * }}} + * @note The parameters API is experimental and may change */ -// REVIEW TODO: make Verilog parameters part of the constructor interface? -abstract class BlackBox extends Module { - // Don't bother taking override_clock|reset, clock/reset locked out anyway - // TODO: actually implement this. - def setVerilogParameters(s: String): Unit = {} +abstract class BlackBox(val params: Map[String, Param] = Map.empty[String, Param]) extends Module { // The body of a BlackBox is empty, the real logic happens in firrtl/Emitter.scala // Bypass standard clock, reset, io port declaration by flattening io diff --git a/chiselFrontend/src/main/scala/chisel3/core/Module.scala b/chiselFrontend/src/main/scala/chisel3/core/Module.scala index ca391091..62b6d5ce 100644 --- a/chiselFrontend/src/main/scala/chisel3/core/Module.scala +++ b/chiselFrontend/src/main/scala/chisel3/core/Module.scala @@ -41,8 +41,16 @@ object Module { sourceInfo.makeMessage(" See " + _)) } Builder.currentModule = parent // Back to parent! + val ports = m.computePorts - val component = Component(m, m.name, ports, m._commands) + // Blackbox inherits from Module so we have to match on it first TODO fix + val component = m match { + case bb: BlackBox => + DefBlackBox(bb, bb.name, ports, bb.params) + case mod: Module => + mod._commands.prepend(DefInvalid(childSourceInfo, mod.io.ref)) // init module outputs + DefModule(mod, mod.name, ports, mod._commands) + } m._component = Some(component) Builder.components += component // Avoid referencing 'parent' in top module diff --git a/chiselFrontend/src/main/scala/chisel3/internal/Builder.scala b/chiselFrontend/src/main/scala/chisel3/internal/Builder.scala index 028ce628..60ce6d5d 100644 --- a/chiselFrontend/src/main/scala/chisel3/internal/Builder.scala +++ b/chiselFrontend/src/main/scala/chisel3/internal/Builder.scala @@ -181,7 +181,10 @@ private[chisel3] object Builder { // TODO(twigg): Ideally, binding checks and new bindings would all occur here // However, rest of frontend can't support this yet. def pushCommand[T <: Command](c: T): T = { - forcedModule._commands += c + forcedModule match { + case _: BlackBox => throwException("Cannot add hardware to a BlackBox") + case m => m._commands += c + } c } def pushOp[T <: Data](cmd: DefPrim[T]): T = { diff --git a/chiselFrontend/src/main/scala/chisel3/internal/firrtl/IR.scala b/chiselFrontend/src/main/scala/chisel3/internal/firrtl/IR.scala index 0f866c27..17b869f2 100644 --- a/chiselFrontend/src/main/scala/chisel3/internal/firrtl/IR.scala +++ b/chiselFrontend/src/main/scala/chisel3/internal/firrtl/IR.scala @@ -215,8 +215,14 @@ case class Connect(sourceInfo: SourceInfo, loc: Node, exp: Arg) extends Command case class BulkConnect(sourceInfo: SourceInfo, loc1: Node, loc2: Node) extends Command case class ConnectInit(sourceInfo: SourceInfo, loc: Node, exp: Arg) extends Command case class Stop(sourceInfo: SourceInfo, clock: Arg, ret: Int) extends Command -case class Component(id: Module, name: String, ports: Seq[Port], commands: Seq[Command]) extends Arg case class Port(id: Data, dir: Direction) case class Printf(sourceInfo: SourceInfo, clock: Arg, pable: Printable) extends Command +abstract class Component extends Arg { + def id: Module + def name: String + def ports: Seq[Port] +} +case class DefModule(id: Module, name: String, ports: Seq[Port], commands: Seq[Command]) extends Component +case class DefBlackBox(id: Module, name: String, ports: Seq[Port], params: Map[String, Param]) extends Component case class Circuit(name: String, components: Seq[Component]) diff --git a/src/main/scala/chisel3/internal/firrtl/Emitter.scala b/src/main/scala/chisel3/internal/firrtl/Emitter.scala index 3fb18893..b8651828 100644 --- a/src/main/scala/chisel3/internal/firrtl/Emitter.scala +++ b/src/main/scala/chisel3/internal/firrtl/Emitter.scala @@ -2,6 +2,7 @@ package chisel3.internal.firrtl import chisel3._ +import chisel3.experimental._ import chisel3.internal.sourceinfo.{NoSourceInfo, SourceLine} private[chisel3] object Emitter { @@ -42,6 +43,16 @@ private class Emitter(circuit: Circuit) { firrtlLine + e.sourceInfo.makeMessage(" " + _) } + private def emitParam(name: String, p: Param): String = { + val str = p match { + case IntParam(value) => value.toString + case DoubleParam(value) => value.toString + case StringParam(str) => "\"" + str + "\"" + case RawParam(str) => "'" + str + "'" + } + s"parameter $name = $str" + } + // Map of Module FIRRTL definition to FIRRTL name, if it has been emitted already. private val defnMap = collection.mutable.HashMap[(String, String), Component]() @@ -61,12 +72,13 @@ private class Emitter(circuit: Circuit) { body ++= newline + emitPort(p) body ++= newline - m.id match { - case _: BlackBox => - // TODO: BlackBoxes should be empty, but funkiness in Module() means - // it's not for now. Eventually, this should assert out. - case _: Module => for (cmd <- m.commands) { - body ++= newline + emit(cmd, m) + m match { + case bb: DefBlackBox => + // Firrtl extmodule can overrule name + body ++= newline + s"defname = ${bb.id.desiredName}" + body ++= newline + (bb.params map { case (n, p) => emitParam(n, p) } mkString newline) + case mod: DefModule => for (cmd <- mod.commands) { + body ++= newline + emit(cmd, mod) } } body ++= newline diff --git a/src/main/scala/chisel3/package.scala b/src/main/scala/chisel3/package.scala index e0364868..3cdda971 100644 --- a/src/main/scala/chisel3/package.scala +++ b/src/main/scala/chisel3/package.scala @@ -179,4 +179,28 @@ package object chisel3 { // scalastyle:ignore package.object.name } def getModulePorts(m: Module): Seq[Port] = m.getPorts def getFirrtlDirection(d: Data): Direction = chisel3.core.Data.getFirrtlDirection(d) + + /** Package for experimental features, which may have their API changed, be removed, etc. + * + * Because its contents won't necessarily have the same level of stability and support as + * non-experimental, you must explicitly import this package to use its contents. + */ + object experimental { + type Param = chisel3.core.Param + type IntParam = chisel3.core.IntParam + val IntParam = chisel3.core.IntParam + type DoubleParam = chisel3.core.DoubleParam + val DoubleParam = chisel3.core.DoubleParam + type StringParam = chisel3.core.StringParam + val StringParam = chisel3.core.StringParam + type RawParam = chisel3.core.RawParam + val RawParam = chisel3.core.RawParam + + // Implicit conversions for BlackBox Parameters + implicit def fromIntToIntParam(x: Int): IntParam = IntParam(BigInt(x)) + implicit def fromLongToIntParam(x: Long): IntParam = IntParam(BigInt(x)) + implicit def fromBigIntToIntParam(x: BigInt): IntParam = IntParam(x) + implicit def fromDoubleToDoubleParam(x: Double): DoubleParam = DoubleParam(x) + implicit def fromStringToStringParam(x: String): StringParam = StringParam(x) + } } diff --git a/src/test/resources/BlackBoxTest.v b/src/test/resources/BlackBoxTest.v index 910b09ff..edf321a8 100644 --- a/src/test/resources/BlackBoxTest.v +++ b/src/test/resources/BlackBoxTest.v @@ -32,3 +32,28 @@ module BlackBoxConstant #( ); assign out = VALUE; endmodule + +module BlackBoxStringParam #( + parameter string STRING = "zero" +) ( + output [31:0] out +); + assign out = (STRING == "one" )? 1 : + (STRING == "two" )? 2 : 0; +endmodule + +module BlackBoxRealParam #( + parameter real REAL = 0.0 +) ( + output [63:0] out +); + assign out = $realtobits(REAL); +endmodule + +module BlackBoxTypeParam #( + parameter type T = bit +) ( + output T out +); + assign out = 32'hdeadbeef; +endmodule diff --git a/src/test/scala/chiselTests/BlackBox.scala b/src/test/scala/chiselTests/BlackBox.scala index 344754e1..d8821134 100644 --- a/src/test/scala/chiselTests/BlackBox.scala +++ b/src/test/scala/chiselTests/BlackBox.scala @@ -6,6 +6,7 @@ import java.io.File import org.scalatest._ import chisel3._ +import chisel3.experimental._ import chisel3.testers.BasicTester import chisel3.util._ //import chisel3.core.ExplicitCompileOptions.Strict @@ -84,27 +85,55 @@ class BlackBoxWithClockTester extends BasicTester { when(end) { stop() } } -/* -// Must determine how to handle parameterized Verilog -class BlackBoxConstant(value: Int) extends BlackBox { - val io = IO(new Bundle() { - val out = Output(UInt(width=log2Up(value))) +class BlackBoxConstant(value: Int) extends BlackBox( + Map("VALUE" -> value, "WIDTH" -> log2Up(value + 1))) { + require(value >= 0, "value must be a UInt!") + val io = IO(new Bundle { + val out = UInt(width = log2Up(value + 1)).asOutput + }) +} + +class BlackBoxStringParam(str: String) extends BlackBox(Map("STRING" -> str)) { + val io = IO(new Bundle { + val out = UInt(width = 32) + }) +} + +class BlackBoxRealParam(dbl: Double) extends BlackBox(Map("REAL" -> dbl)) { + val io = IO(new Bundle { + val out = UInt(width = 64) + }) +} + +class BlackBoxTypeParam(w: Int, raw: String) extends BlackBox(Map("T" -> RawParam(raw))) { + val io = IO(new Bundle { + val out = UInt(width = w) }) - override val name = s"#(WIDTH=${log2Up(value)},VALUE=$value) " } class BlackBoxWithParamsTester extends BasicTester { val blackBoxOne = Module(new BlackBoxConstant(1)) - val blackBoxFour = Module(new BlackBoxConstant(4)) + val blackBoxFour = Module(new BlackBoxConstant(4)) + val blackBoxStringParamOne = Module(new BlackBoxStringParam("one")) + val blackBoxStringParamTwo = Module(new BlackBoxStringParam("two")) + val blackBoxRealParamOne = Module(new BlackBoxRealParam(1.0)) + val blackBoxRealParamNeg = Module(new BlackBoxRealParam(-1.0)) + val blackBoxTypeParamBit = Module(new BlackBoxTypeParam(1, "bit")) + val blackBoxTypeParamWord = Module(new BlackBoxTypeParam(32, "bit [31:0]")) val (cycles, end) = Counter(Bool(true), 4) assert(blackBoxOne.io.out === UInt(1)) assert(blackBoxFour.io.out === UInt(4)) + assert(blackBoxStringParamOne.io.out === UInt(1)) + assert(blackBoxStringParamTwo.io.out === UInt(2)) + assert(blackBoxRealParamOne.io.out === UInt(0x3ff0000000000000L)) + assert(blackBoxRealParamNeg.io.out === UInt(BigInt("bff0000000000000", 16))) + assert(blackBoxTypeParamBit.io.out === UInt(1)) + assert(blackBoxTypeParamWord.io.out === UInt("hdeadbeef", 32)) when(end) { stop() } } -*/ class BlackBoxSpec extends ChiselFlatSpec { "A BlackBoxed inverter" should "work" in { @@ -119,4 +148,8 @@ class BlackBoxSpec extends ChiselFlatSpec { assertTesterPasses({ new BlackBoxWithClockTester }, Seq("/BlackBoxTest.v")) } + "BlackBoxes with parameters" should "work" in { + assertTesterPasses({ new BlackBoxWithParamsTester }, + Seq("/BlackBoxTest.v")) + } } |
