summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--chiselFrontend/src/main/scala/chisel3/core/Data.scala127
-rw-r--r--chiselFrontend/src/main/scala/chisel3/core/Reg.scala117
-rw-r--r--src/test/scala/chiselTests/ChiselSpec.scala81
-rw-r--r--src/test/scala/chiselTests/WidthSpec.scala171
4 files changed, 465 insertions, 31 deletions
diff --git a/chiselFrontend/src/main/scala/chisel3/core/Data.scala b/chiselFrontend/src/main/scala/chisel3/core/Data.scala
index f292d3c6..e56412e6 100644
--- a/chiselFrontend/src/main/scala/chisel3/core/Data.scala
+++ b/chiselFrontend/src/main/scala/chisel3/core/Data.scala
@@ -429,9 +429,9 @@ abstract class Data extends HasId with NamedComponent with SourceInfoDoc {
*/
def litValue(): BigInt = litOption.get
- /** Returns the width, in bits, if currently known.
- * @throws java.util.NoSuchElementException if the width is not known. */
- final def getWidth: Int = width.get
+ /** Returns the width, in bits, if currently known. */
+ final def getWidth: Int =
+ if (isWidthKnown) width.get else throwException(s"Width of $this is unknown!")
/** Returns whether the width is currently known. */
final def isWidthKnown: Boolean = width.known
/** Returns Some(width) if the width is known, else None. */
@@ -484,6 +484,10 @@ abstract class Data extends HasId with NamedComponent with SourceInfoDoc {
}
trait WireFactory {
+ /** @usecase def apply[T <: Data](t: T): T
+ * Construct a [[Wire]] from a type template
+ * @param t The template from which to construct this wire
+ */
def apply[T <: Data](t: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = {
if (compileOptions.declaredTypeMustBeUnbound) {
requireIsChiselType(t, "wire type")
@@ -502,17 +506,88 @@ trait WireFactory {
}
}
+/** Utility for constructing hardware wires
+ *
+ * The width of a `Wire` (inferred or not) is copied from the type template
+ * {{{
+ * val w0 = Wire(UInt()) // width is inferred
+ * val w1 = Wire(UInt(8.W)) // width is set to 8
+ *
+ * val w2 = Wire(Vec(4, UInt())) // width is inferred
+ * val w3 = Wire(Vec(4, UInt(8.W))) // width of each element is set to 8
+ *
+ * class MyBundle {
+ * val unknown = UInt()
+ * val known = UInt(8.W)
+ * }
+ * val w4 = Wire(new MyBundle)
+ * // Width of w4.unknown is inferred
+ * // Width of w4.known is set to 8
+ * }}}
+ *
+ */
object Wire extends WireFactory
+/** Utility for constructing hardware wires with a default connection
+ *
+ * The two forms of `WireInit` differ in how the type and width of the resulting [[Wire]] are
+ * specified.
+ *
+ * ==Single Argument==
+ * The single argument form uses the argument to specify both the type and default connection. For
+ * non-literal [[Bits]], the width of the [[Wire]] will be inferred. For literal [[Bits]] and all
+ * non-Bits arguments, the type will be copied from the argument. See the following examples for
+ * more details:
+ *
+ * 1. Literal [[Bits]] initializer: width will be set to match
+ * {{{
+ * val w1 = WireInit(1.U) // width will be inferred to be 1
+ * val w2 = WireInit(1.U(8.W)) // width is set to 8
+ * }}}
+ *
+ * 2. Non-Literal [[Element]] initializer - width will be inferred
+ * {{{
+ * val x = Wire(UInt())
+ * val y = Wire(UInt(8.W))
+ * val w1 = WireInit(x) // width will be inferred
+ * val w2 = WireInit(y) // width will be inferred
+ * }}}
+ *
+ * 3. [[Aggregate]] initializer - width will be set to match the aggregate
+ *
+ * {{{
+ * class MyBundle {
+ * val unknown = UInt()
+ * val known = UInt(8.W)
+ * }
+ * val w1 = Wire(new MyBundle)
+ * val w2 = WireInit(w1)
+ * // Width of w2.unknown is inferred
+ * // Width of w2.known is set to 8
+ * }}}
+ *
+ * ==Double Argument==
+ * The double argument form allows the type of the [[Wire]] and the default connection to be
+ * specified independently.
+ *
+ * The width inference semantics for `WireInit` with two arguments match those of [[Wire]]. The
+ * first argument to `WireInit` is the type template which defines the width of the `Wire` in
+ * exactly the same way as the only argument to [[Wire]].
+ *
+ * More explicitly, you can reason about `WireInit` with multiple arguments as if it were defined
+ * as:
+ * {{{
+ * def WireInit[T <: Data](t: T, init: T): T = {
+ * val x = Wire(t)
+ * x := init
+ * x
+ * }
+ * }}}
+ *
+ * @note The `Init` in `WireInit` refers to a `default` connection. This is in contrast to
+ * [[RegInit]] where the `Init` refers to a value on reset.
+ */
object WireInit {
- def apply[T <: Data](init: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = {
- val model = (init match {
- // For e.g. Wire(init=0.U(k.W)), fix the Reg's width to k
- case init: Bits if init.litIsForcedWidth == Some(false) => init.cloneTypeWidth(Width())
- case _ => init.cloneTypeFull
- }).asInstanceOf[T]
- apply(model, init)
- }
private def applyImpl[T <: Data](t: T, init: Data)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = {
implicit val noSourceInfo = UnlocatableSourceInfo
@@ -522,12 +597,38 @@ object WireInit {
x
}
- def apply[T <: Data](t: T, init: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = {
+ /** @usecase def apply[T <: Data](t: T, init: DontCare.type): T
+ * Construct a [[Wire]] with a type template and a [[DontCare]] default
+ * @param t The type template used to construct this [[Wire]]
+ * @param init The default connection to this [[Wire]], can only be [[DontCare]]
+ * @note This is really just a specialized form of `apply[T <: Data](t: T, init: T): T` with [[DontCare]]
+ * as `init`
+ */
+ def apply[T <: Data](t: T, init: DontCare.type)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = {
applyImpl(t, init)
}
- def apply[T <: Data](t: T, init: DontCare.type)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = {
+
+ /** @usecase def apply[T <: Data](t: T, init: T): T
+ * Construct a [[Wire]] with a type template and a default connection
+ * @param t The type template used to construct this [[Wire]]
+ * @param init The hardware value that will serve as the default value
+ */
+ def apply[T <: Data](t: T, init: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = {
applyImpl(t, init)
}
+
+ /** @usecase def apply[T <: Data](init: T): T
+ * Construct a [[Wire]] with a default connection
+ * @param init The hardware value that will serve as a type template and default value
+ */
+ def apply[T <: Data](init: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = {
+ val model = (init match {
+ // If init is a literal without forced width OR any non-literal, let width be inferred
+ case init: Bits if !init.litIsForcedWidth.getOrElse(false) => init.cloneTypeWidth(Width())
+ case _ => init.cloneTypeFull
+ }).asInstanceOf[T]
+ apply(model, init)
+ }
}
/** RHS (source) for Invalidate API.
diff --git a/chiselFrontend/src/main/scala/chisel3/core/Reg.scala b/chiselFrontend/src/main/scala/chisel3/core/Reg.scala
index 11611c82..27156f8b 100644
--- a/chiselFrontend/src/main/scala/chisel3/core/Reg.scala
+++ b/chiselFrontend/src/main/scala/chisel3/core/Reg.scala
@@ -9,11 +9,31 @@ import chisel3.internal.Builder.pushCommand
import chisel3.internal.firrtl._
import chisel3.internal.sourceinfo.{SourceInfo}
+/** Utility for constructing hardware registers
+ *
+ * The width of a `Reg` (inferred or not) is copied from the type template
+ * {{{
+ * val r0 = Reg(UInt()) // width is inferred
+ * val r1 = Reg(UInt(8.W)) // width is set to 8
+ *
+ * val r2 = Reg(Vec(4, UInt())) // width is inferred
+ * val r3 = Reg(Vec(4, UInt(8.W))) // width of each element is set to 8
+ *
+ * class MyBundle {
+ * val unknown = UInt()
+ * val known = UInt(8.W)
+ * }
+ * val r4 = Reg(new MyBundle)
+ * // Width of r4.unknown is inferred
+ * // Width of r4.known is set to 8
+ * }}}
+ *
+ */
object Reg {
- /** Creates a register without initialization (reset is ignored). Value does
- * not change unless assigned to (using the := operator).
- *
- * @param t: data type for the register
+ /** @usecase def apply[T <: Data](t: T): T
+ * Construct a [[Reg]] from a type template with no initialization value (reset is ignored).
+ * Value will not change unless the [[Reg]] is given a connection.
+ * @param t The template from which to construct this wire
*/
def apply[T <: Data](t: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = {
if (compileOptions.declaredTypeMustBeUnbound) {
@@ -64,20 +84,69 @@ object RegNext {
}
}
+/** Utility for constructing hardware registers with an initialization value.
+ *
+ * The register is set to the initialization value when the current implicit `reset` is high
+ *
+ * The two forms of `RegInit` differ in how the type and width of the resulting [[Reg]] are
+ * specified.
+ *
+ * ==Single Argument==
+ * The single argument form uses the argument to specify both the type and reset value. For
+ * non-literal [[Bits]], the width of the [[Reg]] will be inferred. For literal [[Bits]] and all
+ * non-Bits arguments, the type will be copied from the argument. See the following examples for
+ * more details:
+ *
+ * 1. Literal [[Bits]] initializer: width will be set to match
+ * {{{
+ * val r1 = RegInit(1.U) // width will be inferred to be 1
+ * val r2 = RegInit(1.U(8.W)) // width is set to 8
+ * }}}
+ *
+ * 2. Non-Literal [[Element]] initializer - width will be inferred
+ * {{{
+ * val x = Wire(UInt())
+ * val y = Wire(UInt(8.W))
+ * val r1 = RegInit(x) // width will be inferred
+ * val r2 = RegInit(y) // width will be inferred
+ * }}}
+ *
+ * 3. [[Aggregate]] initializer - width will be set to match the aggregate
+ *
+ * {{{
+ * class MyBundle {
+ * val unknown = UInt()
+ * val known = UInt(8.W)
+ * }
+ * val w1 = Reg(new MyBundle)
+ * val w2 = RegInit(w1)
+ * // Width of w2.unknown is inferred
+ * // Width of w2.known is set to 8
+ * }}}
+ *
+ * ==Double Argument==
+ * The double argument form allows the type of the [[Reg]] and the default connection to be
+ * specified independently.
+ *
+ * The width inference semantics for `RegInit` with two arguments match those of [[Reg]]. The
+ * first argument to `RegInit` is the type template which defines the width of the `Reg` in
+ * exactly the same way as the only argument to [[Wire]].
+ *
+ * More explicitly, you can reason about `RegInit` with multiple arguments as if it were defined
+ * as:
+ * {{{
+ * def RegInit[T <: Data](t: T, init: T): T = {
+ * val x = Reg(t)
+ * x := init
+ * x
+ * }
+ * }}}
+ */
object RegInit {
- /** Returns a register pre-initialized (on reset) to the specified value.
- * Register type is inferred from the initializer.
- */
- def apply[T <: Data](init: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = {
- val model = (init match {
- // For e.g. Reg(init=UInt(0, k)), fix the Reg's width to k
- case init: Bits if init.litIsForcedWidth == Some(false) => init.cloneTypeWidth(Width())
- case init => init.cloneTypeFull
- }).asInstanceOf[T]
- RegInit(model, init)
- }
-
- /** Creates a register given an explicit type and an initialization (reset) value.
+ /** @usecase def apply[T <: Data](t: T, init: T): T
+ * 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) {
@@ -92,4 +161,18 @@ object RegInit {
pushCommand(DefRegInit(sourceInfo, reg, clock, reset, init.ref))
reg
}
+
+ /** @usecase def apply[T <: Data](init: T): T
+ * Construct a [[Reg]] initialized on reset to the specified value.
+ * @param init Initial value that serves as a type template and reset value
+ */
+ def apply[T <: Data](init: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = {
+ val model = (init match {
+ // If init is a literal without forced width OR any non-literal, let width be inferred
+ case init: Bits if !init.litIsForcedWidth.getOrElse(false) => init.cloneTypeWidth(Width())
+ case init => init.cloneTypeFull
+ }).asInstanceOf[T]
+ RegInit(model, init)
+ }
+
}
diff --git a/src/test/scala/chiselTests/ChiselSpec.scala b/src/test/scala/chiselTests/ChiselSpec.scala
index 661cf00e..ff75c1a3 100644
--- a/src/test/scala/chiselTests/ChiselSpec.scala
+++ b/src/test/scala/chiselTests/ChiselSpec.scala
@@ -31,6 +31,33 @@ trait ChiselRunners extends Assertions with BackendCompilationUtilities {
}
def elaborate(t: => RawModule): Unit = Driver.elaborate(() => t)
+ def assertKnownWidth(expected: Int)(gen: => Data): Unit = {
+ assertTesterPasses(new BasicTester {
+ val x = gen
+ assert(x.getWidth === expected)
+ // Sanity check that firrtl doesn't change the width
+ x := 0.U.asTypeOf(chiselTypeOf(x))
+ val (_, done) = chisel3.util.Counter(true.B, 2)
+ when (done) {
+ chisel3.assert(~(x.asUInt) === -1.S(expected.W).asUInt)
+ stop()
+ }
+ })
+ }
+
+ def assertInferredWidth(expected: Int)(gen: => Data): Unit = {
+ assertTesterPasses(new BasicTester {
+ val x = gen
+ assert(!x.isWidthKnown, s"Asserting that width should be inferred yet width is known to Chisel!")
+ x := 0.U.asTypeOf(chiselTypeOf(x))
+ val (_, done) = chisel3.util.Counter(true.B, 2)
+ when (done) {
+ chisel3.assert(~(x.asUInt) === -1.S(expected.W).asUInt)
+ stop()
+ }
+ })
+ }
+
/** Given a generator, return the Firrtl that it generates.
*
* @param t Module generator
@@ -63,7 +90,59 @@ trait ChiselRunners extends Assertions with BackendCompilationUtilities {
}
/** Spec base class for BDD-style testers. */
-class ChiselFlatSpec extends FlatSpec with ChiselRunners with Matchers
+abstract class ChiselFlatSpec extends FlatSpec with ChiselRunners with Matchers
+
+class ChiselTestUtilitiesSpec extends ChiselFlatSpec {
+ import org.scalatest.exceptions.TestFailedException
+ // Who tests the testers?
+ "assertKnownWidth" should "error when the expected width is wrong" in {
+ a [TestFailedException] shouldBe thrownBy {
+ assertKnownWidth(7) {
+ Wire(UInt(8.W))
+ }
+ }
+ }
+
+ it should "error when the width is unknown" in {
+ a [ChiselException] shouldBe thrownBy {
+ assertKnownWidth(7) {
+ Wire(UInt())
+ }
+ }
+ }
+
+ it should "work if the width is correct" in {
+ assertKnownWidth(8) {
+ Wire(UInt(8.W))
+ }
+ }
+
+ "assertInferredWidth" should "error if the width is known" in {
+ a [TestFailedException] shouldBe thrownBy {
+ assertInferredWidth(8) {
+ Wire(UInt(8.W))
+ }
+ }
+ }
+
+ it should "error if the expected width is wrong" in {
+ a [TestFailedException] shouldBe thrownBy {
+ assertInferredWidth(8) {
+ val w = Wire(UInt())
+ w := 2.U(2.W)
+ w
+ }
+ }
+ }
+
+ it should "pass if the width is correct" in {
+ assertInferredWidth(4) {
+ val w = Wire(UInt())
+ w := 2.U(4.W)
+ w
+ }
+ }
+}
/** Spec base class for property-based testers. */
class ChiselPropSpec extends PropSpec with ChiselRunners with PropertyChecks with Matchers {
diff --git a/src/test/scala/chiselTests/WidthSpec.scala b/src/test/scala/chiselTests/WidthSpec.scala
index 9a5b1860..0011c532 100644
--- a/src/test/scala/chiselTests/WidthSpec.scala
+++ b/src/test/scala/chiselTests/WidthSpec.scala
@@ -4,6 +4,19 @@ package chiselTests
import chisel3._
+class SimpleBundle extends Bundle {
+ val x = UInt(4.W)
+ val y = UInt()
+}
+object SimpleBundle {
+ def intoWire(): SimpleBundle = {
+ val w = Wire(new SimpleBundle)
+ w.x := 0.U(4.W)
+ w.y := 0.U(4.W)
+ w
+ }
+}
+
class WidthSpec extends ChiselFlatSpec {
"Literals without specified widths" should "get the minimum legal width" in {
"hdeadbeef".U.getWidth should be (32)
@@ -15,3 +28,161 @@ class WidthSpec extends ChiselFlatSpec {
1.S.getWidth should be (2)
}
}
+
+abstract class WireRegWidthSpecImpl extends ChiselFlatSpec {
+ def name: String
+ def builder[T <: Data](x: T): T
+
+ behavior of name
+
+ it should "set the width if the template type has a set width" in {
+ assertKnownWidth(4) {
+ builder(UInt(4.W))
+ }
+ assertKnownWidth(4) {
+ val w = builder(new SimpleBundle)
+ w := SimpleBundle.intoWire()
+ w.x
+ }
+ assertKnownWidth(4) {
+ val x = builder(Vec(1, UInt(4.W)))
+ x(0)
+ }
+ }
+
+ it should "infer the width if the template type has no width" in {
+ assertInferredWidth(4) {
+ val w = builder(UInt())
+ w := 0.U(4.W)
+ w
+ }
+ assertInferredWidth(4) {
+ val w = builder(new SimpleBundle)
+ w := SimpleBundle.intoWire()
+ w.y
+ }
+ assertInferredWidth(4) {
+ val w = builder(Vec(1, UInt()))
+ w(0) := 0.U(4.W)
+ w(0)
+ }
+ }
+}
+
+class WireWidthSpec extends WireRegWidthSpecImpl {
+ def name = "Wire"
+ def builder[T <: Data](x: T): T = Wire(x)
+}
+class RegWidthSpec extends WireRegWidthSpecImpl {
+ def name = "Reg"
+ def builder[T <: Data](x: T): T = Reg(x)
+}
+
+abstract class WireInitRegInitSpecImpl extends ChiselFlatSpec {
+ def name: String
+ def builder1[T <: Data](x: T): T
+ def builder2[T <: Data](x: T, y: T): T
+
+ behavior of s"$name (Single Argument)"
+
+ it should "set width if passed a literal with forced width" in {
+ assertKnownWidth(4) {
+ builder1(3.U(4.W))
+ }
+ }
+
+ it should "NOT set width if passed a literal without a forced width" in {
+ assertInferredWidth(4) {
+ val w = builder1(3.U)
+ w := 3.U(4.W)
+ w
+ }
+ }
+
+ it should "NOT set width if passed a non-literal" in {
+ assertInferredWidth(4) {
+ val w = WireInit(3.U(4.W))
+ builder1(w)
+ }
+ }
+
+ it should "copy the widths of aggregates" in {
+ assertInferredWidth(4) {
+ val w = builder1(SimpleBundle.intoWire())
+ w.y
+ }
+ assertKnownWidth(4) {
+ val w = builder1(SimpleBundle.intoWire())
+ w.x
+ }
+ assertInferredWidth(4) {
+ val x = Wire(Vec(1, UInt()))
+ x(0) := 0.U(4.W)
+ val w = builder1(x)
+ w(0)
+ }
+ assertKnownWidth(4) {
+ val x = Wire(Vec(1, UInt(4.W)))
+ x(0) := 0.U(4.W)
+ val w = builder1(x)
+ w(0)
+ }
+ }
+
+ behavior of s"$name (Double Argument)"
+
+ it should "set the width if the template type has a set width" in {
+ assertKnownWidth(4) {
+ WireInit(UInt(4.W), 0.U)
+ }
+ assertKnownWidth(4) {
+ WireInit(UInt(4.W), 0.U(2.W))
+ }
+ assertKnownWidth(4) {
+ val w = WireInit(new SimpleBundle, SimpleBundle.intoWire())
+ w.x
+ }
+ assertKnownWidth(4) {
+ val x = Wire(Vec(1, UInt()))
+ x(0) := 0.U(4.W)
+ val w = WireInit(Vec(1, UInt(4.W)), x)
+ w(0)
+ }
+ }
+
+ it should "infer the width if the template type has no width" in {
+ val templates = Seq(
+ () => 0.U, () => 0.U(2.W), () => WireInit(0.U), () => WireInit(0.U(2.W))
+ )
+ for (gen <- templates) {
+ assertInferredWidth(4) {
+ val w = WireInit(UInt(), gen())
+ w := 0.U(4.W)
+ w
+ }
+ }
+ assertInferredWidth(4) {
+ val w = WireInit(new SimpleBundle, SimpleBundle.intoWire())
+ w.y
+ }
+ assertInferredWidth(4) {
+ val x = Wire(Vec(1, UInt()))
+ x(0) := 0.U(4.W)
+ val w = WireInit(Vec(1, UInt()), x)
+ w(0)
+ }
+ }
+}
+
+class WireInitWidthSpec extends WireInitRegInitSpecImpl {
+ def name = "WireInit"
+ def builder1[T <: Data](x: T): T = WireInit(x)
+ def builder2[T <: Data](x: T, y: T): T = WireInit(x, y)
+}
+
+class RegInitWidthSpec extends WireInitRegInitSpecImpl {
+ def name = "RegInit"
+ def builder1[T <: Data](x: T): T = RegInit(x)
+ def builder2[T <: Data](x: T, y: T): T = RegInit(x, y)
+}
+