summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdam Izraelevitz2017-10-26 12:39:42 -0700
committerJim Lawson2017-10-26 12:39:42 -0700
commitc313e137d4e562ef20195312501840ceab8cbc6a (patch)
tree37e290d3c5af672624b9ac267ccb33421acca84e
parent8168a8eea6c3465966081c5acd0347e09791361c (diff)
Invalidateapi (#645)
* Require explicit connection to DontCare to generate "is invalid". * Add tests for RefNotInitializedException. Currently, we fail the when ... otherwise ... * Disable ScalaTest shrinking on error in ComplexAssignSpec. * fix broken merge; still some binding issues * cleanup DontCare connection checks; add missing directions to test module IOs * Have library code inherit compileOptions from the enclosing Module (if it exists). * work around current firrtl uninitialized references with Strict compile options and explicitInvalidate * more CompileOptions cleanup; move test-specific defines to package object * minimize differences with master * set default CompileOptions.explicitInvalidate to false until we fix the FIRRTL when issue * ignore the StrictCompiler property checks (until CompileOptions.explicitInvalidate is defaulted to true) * Revert "more CompileOptions cleanup; move test-specific defines to package object" This reverts commit e4486edcba990d150e76e08a2fc6abca033556e0. * Revert "work around current firrtl uninitialized references with Strict compile options and explicitInvalidate" This reverts commit 426faa430a62c3dac2dbdf33044d3386d4243157. * remove unused code * Convert to binding-based DontCare implementation * comment cleanup to minimize differences with master * Tentatively remove possibly redundant DefInvalid on module ports. * Respond to code review change request. - backout build.sbt change - correct indentation - handle bulk of DontCare semantics in elemConnect() - have DontCare extend Element, not Data (eliminate most Object specific methods - add comments indicating reason for explicit DontCare connections * Initialize test elements without requiring a DontCare. * Respond to review change requests. - DontCare should work on left or right side in BiDirectional connections - call bind() to set DontCare binding instead of messing with internal variables - DontCares are only equivalent with DontCares - clean up processWhens() definition * Eliminate DontCare connection to inputs in MonoConnect(). * Pull aggregates apart for the purpose of DontCare connections. * Restore the explicit (conditionally executed) ports DefInvalidin ImplicitModule() * Don't add DontCare's to the module list of _ids. * Add missing DefInvalid() to LegacyModule(). * Respond to review requests: add DontCare BiConnect Vec, remove null parent hack to avoid addId(), initialize singletons early in Builder * Move DontCare out of chisel3.experimental.
-rw-r--r--chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala11
-rw-r--r--chiselFrontend/src/main/scala/chisel3/core/BiConnect.scala87
-rw-r--r--chiselFrontend/src/main/scala/chisel3/core/Binding.scala3
-rw-r--r--chiselFrontend/src/main/scala/chisel3/core/Bits.scala13
-rw-r--r--chiselFrontend/src/main/scala/chisel3/core/CompileOptions.scala12
-rw-r--r--chiselFrontend/src/main/scala/chisel3/core/Data.scala37
-rw-r--r--chiselFrontend/src/main/scala/chisel3/core/MonoConnect.scala35
-rw-r--r--chiselFrontend/src/main/scala/chisel3/core/UserModule.scala23
-rw-r--r--chiselFrontend/src/main/scala/chisel3/internal/Builder.scala5
-rw-r--r--src/main/scala/chisel3/internal/firrtl/Emitter.scala14
-rw-r--r--src/main/scala/chisel3/package.scala3
-rw-r--r--src/main/scala/chisel3/util/Decoupled.scala2
-rw-r--r--src/test/scala/chiselTests/ComplexAssign.scala4
-rw-r--r--src/test/scala/chiselTests/DontTouchSpec.scala1
-rw-r--r--src/test/scala/chiselTests/FixedPointSpec.scala2
-rw-r--r--src/test/scala/chiselTests/InvalidateAPISpec.scala164
-rw-r--r--src/test/scala/chiselTests/Vec.scala3
-rw-r--r--src/test/scala/chiselTests/VectorPacketIO.scala5
-rw-r--r--src/test/scala/chiselTests/When.scala3
19 files changed, 390 insertions, 37 deletions
diff --git a/chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala b/chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala
index 47f4b3e7..88cc6ff6 100644
--- a/chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala
+++ b/chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala
@@ -67,8 +67,15 @@ sealed abstract class Aggregate extends Data {
def getElements: Seq[Data]
private[chisel3] def width: Width = getElements.map(_.width).foldLeft(0.W)(_ + _)
- private[core] def legacyConnect(that: Data)(implicit sourceInfo: SourceInfo): Unit =
- pushCommand(BulkConnect(sourceInfo, this.lref, that.lref))
+ private[core] def legacyConnect(that: Data)(implicit sourceInfo: SourceInfo): Unit = {
+ // If the source is a DontCare, generate a DefInvalid for the sink,
+ // otherwise, issue a Connect.
+ if (that == DontCare) {
+ pushCommand(DefInvalid(sourceInfo, this.lref))
+ } else {
+ pushCommand(BulkConnect(sourceInfo, this.lref, that.lref))
+ }
+ }
override def do_asUInt(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = {
SeqUtils.do_asUInt(flatten.map(_.asUInt()))
diff --git a/chiselFrontend/src/main/scala/chisel3/core/BiConnect.scala b/chiselFrontend/src/main/scala/chisel3/core/BiConnect.scala
index f2b2b7e1..d056efba 100644
--- a/chiselFrontend/src/main/scala/chisel3/core/BiConnect.scala
+++ b/chiselFrontend/src/main/scala/chisel3/core/BiConnect.scala
@@ -2,10 +2,8 @@
package chisel3.core
-import chisel3.internal.Builder
import chisel3.internal.Builder.pushCommand
-import chisel3.internal.firrtl.{Attach, Connect}
-import chisel3.internal.throwException
+import chisel3.internal.firrtl.{Connect, DefInvalid}
import scala.language.experimental.macros
import chisel3.internal.sourceinfo._
@@ -46,6 +44,8 @@ object BiConnect {
BiConnectException(s": Left ($left) and Right ($right) have different types.")
def AttachAlreadyBulkConnectedException(sourceInfo: SourceInfo) =
BiConnectException(sourceInfo.makeMessage(": Analog previously bulk connected at " + _))
+ def DontCareCantBeSink =
+ BiConnectException(": DontCare cannot be a connection sink (LHS)")
/** This function is what recursively tries to connect a left and right together
@@ -54,7 +54,7 @@ object BiConnect {
* during the recursive decent and then rethrow them with extra information added.
* This gives the user a 'path' to where in the connections things went wrong.
*/
- def connect(sourceInfo: SourceInfo, connectCompileOptions: CompileOptions, left: Data, right: Data, context_mod: UserModule): Unit =
+ def connect(sourceInfo: SourceInfo, connectCompileOptions: CompileOptions, left: Data, right: Data, context_mod: UserModule): Unit = {
(left, right) match {
// Handle element case (root case)
case (left_a: Analog, right_a: Analog) =>
@@ -69,9 +69,11 @@ object BiConnect {
// TODO(twigg): Verify the element-level classes are connectable
}
// Handle Vec case
- case (left_v: Vec[Data @unchecked], right_v: Vec[Data @unchecked]) => {
- if(left_v.length != right_v.length) { throw MismatchedVecException }
- for(idx <- 0 until left_v.length) {
+ case (left_v: Vec[Data@unchecked], right_v: Vec[Data@unchecked]) => {
+ if (left_v.length != right_v.length) {
+ throw MismatchedVecException
+ }
+ for (idx <- 0 until left_v.length) {
try {
implicit val compileOptions = connectCompileOptions
connect(sourceInfo, connectCompileOptions, left_v(idx), right_v(idx), context_mod)
@@ -80,6 +82,28 @@ object BiConnect {
}
}
}
+ // Handle Vec connected to DontCare
+ case (left_v: Vec[Data@unchecked], DontCare) => {
+ for (idx <- 0 until left_v.length) {
+ try {
+ implicit val compileOptions = connectCompileOptions
+ connect(sourceInfo, connectCompileOptions, left_v(idx), right, context_mod)
+ } catch {
+ case BiConnectException(message) => throw BiConnectException(s"($idx)$message")
+ }
+ }
+ }
+ // Handle DontCare connected to Vec
+ case (DontCare, right_v: Vec[Data@unchecked]) => {
+ for (idx <- 0 until right_v.length) {
+ try {
+ implicit val compileOptions = connectCompileOptions
+ connect(sourceInfo, connectCompileOptions, left, right_v(idx), context_mod)
+ } catch {
+ case BiConnectException(message) => throw BiConnectException(s"($idx)$message")
+ }
+ }
+ }
// Handle Records defined in Chisel._ code (change to NotStrict)
case (left_r: Record, right_r: Record) => (left_r.compileOptions, right_r.compileOptions) match {
case (ExplicitCompileOptions.NotStrict, _) =>
@@ -89,9 +113,40 @@ object BiConnect {
case _ => recordConnect(sourceInfo, connectCompileOptions, left_r, right_r, context_mod)
}
+ // Handle Records connected to DontCare (change to NotStrict)
+ case (left_r: Record, DontCare) =>
+ left_r.compileOptions match {
+ case ExplicitCompileOptions.NotStrict =>
+ left.bulkConnect(right)(sourceInfo, ExplicitCompileOptions.NotStrict)
+ case _ =>
+ // For each field in left, descend with right
+ for ((field, left_sub) <- left_r.elements) {
+ try {
+ connect(sourceInfo, connectCompileOptions, left_sub, right, context_mod)
+ } catch {
+ case BiConnectException(message) => throw BiConnectException(s".$field$message")
+ }
+ }
+ }
+ case (DontCare, right_r: Record) =>
+ right_r.compileOptions match {
+ case ExplicitCompileOptions.NotStrict =>
+ left.bulkConnect(right)(sourceInfo, ExplicitCompileOptions.NotStrict)
+ case _ =>
+ // For each field in left, descend with right
+ for ((field, right_sub) <- right_r.elements) {
+ try {
+ connect(sourceInfo, connectCompileOptions, left, right_sub, context_mod)
+ } catch {
+ case BiConnectException(message) => throw BiConnectException(s".$field$message")
+ }
+ }
+ }
+
// Left and right are different subtypes of Data so fail
case (left, right) => throw MismatchedException(left.toString, right.toString)
}
+ }
// Do connection of two Records
def recordConnect(sourceInfo: SourceInfo,
@@ -128,11 +183,25 @@ object BiConnect {
// These functions (finally) issue the connection operation
// Issue with right as sink, left as source
private def issueConnectL2R(left: Element, right: Element)(implicit sourceInfo: SourceInfo): Unit = {
- pushCommand(Connect(sourceInfo, right.lref, left.ref))
+ // Source and sink are ambiguous in the case of a Bi/Bulk Connect (<>).
+ // If either is a DontCareBinding, just issue a DefInvalid for the other,
+ // otherwise, issue a Connect.
+ (left.binding, right.binding) match {
+ case (lb: DontCareBinding, _) => pushCommand(DefInvalid(sourceInfo, right.lref))
+ case (_, rb: DontCareBinding) => pushCommand(DefInvalid(sourceInfo, left.lref))
+ case (_, _) => pushCommand(Connect(sourceInfo, right.lref, left.ref))
+ }
}
// Issue with left as sink, right as source
private def issueConnectR2L(left: Element, right: Element)(implicit sourceInfo: SourceInfo): Unit = {
- pushCommand(Connect(sourceInfo, left.lref, right.ref))
+ // Source and sink are ambiguous in the case of a Bi/Bulk Connect (<>).
+ // If either is a DontCareBinding, just issue a DefInvalid for the other,
+ // otherwise, issue a Connect.
+ (left.binding, right.binding) match {
+ case (lb: DontCareBinding, _) => pushCommand(DefInvalid(sourceInfo, right.lref))
+ case (_, rb: DontCareBinding) => pushCommand(DefInvalid(sourceInfo, left.lref))
+ case (_, _) => pushCommand(Connect(sourceInfo, left.lref, right.ref))
+ }
}
// This function checks if element-level connection operation allowed.
diff --git a/chiselFrontend/src/main/scala/chisel3/core/Binding.scala b/chiselFrontend/src/main/scala/chisel3/core/Binding.scala
index b09bab0e..3fdc383c 100644
--- a/chiselFrontend/src/main/scala/chisel3/core/Binding.scala
+++ b/chiselFrontend/src/main/scala/chisel3/core/Binding.scala
@@ -104,3 +104,6 @@ case class WireBinding(enclosure: UserModule) extends ConstrainedBinding
case class ChildBinding(parent: Data) extends Binding {
def location = parent.binding.location
}
+// A DontCare element has a specific Binding, somewhat like a literal.
+// It is a source (RHS). It may only be connected/applied to sinks.
+case class DontCareBinding() extends UnconstrainedBinding
diff --git a/chiselFrontend/src/main/scala/chisel3/core/Bits.scala b/chiselFrontend/src/main/scala/chisel3/core/Bits.scala
index 090c320b..4df46b75 100644
--- a/chiselFrontend/src/main/scala/chisel3/core/Bits.scala
+++ b/chiselFrontend/src/main/scala/chisel3/core/Bits.scala
@@ -32,8 +32,15 @@ abstract class Element(private[chisel3] val width: Width) extends Data {
def widthKnown: Boolean = width.known
def name: String = getRef.name
- private[core] def legacyConnect(that: Data)(implicit sourceInfo: SourceInfo): Unit =
- pushCommand(Connect(sourceInfo, this.lref, that.ref))
+ private[core] def legacyConnect(that: Data)(implicit sourceInfo: SourceInfo): Unit = {
+ // If the source is a DontCare, generate a DefInvalid for the sink,
+ // otherwise, issue a Connect.
+ if (that == DontCare) {
+ pushCommand(DefInvalid(sourceInfo, this.lref))
+ } else {
+ pushCommand(Connect(sourceInfo, this.lref, that.ref))
+ }
+ }
}
/** Exists to unify common interfaces of [[Bits]] and [[Reset]]
@@ -799,7 +806,7 @@ sealed class FixedPoint private (width: Width, val binaryPoint: BinaryPoint, lit
new FixedPoint(w, binaryPoint).asInstanceOf[this.type]
override def connect (that: Data)(implicit sourceInfo: SourceInfo, connectCompileOptions: CompileOptions): Unit = that match {
- case _: FixedPoint => super.connect(that)
+ case _: FixedPoint|DontCare => super.connect(that)
case _ => this badConnect that
}
diff --git a/chiselFrontend/src/main/scala/chisel3/core/CompileOptions.scala b/chiselFrontend/src/main/scala/chisel3/core/CompileOptions.scala
index 55e6d18c..e22519d9 100644
--- a/chiselFrontend/src/main/scala/chisel3/core/CompileOptions.scala
+++ b/chiselFrontend/src/main/scala/chisel3/core/CompileOptions.scala
@@ -18,6 +18,8 @@ trait CompileOptions {
val dontAssumeDirectionality: Boolean
// Check that referenced Data have actually been declared.
val checkSynthesizable: Boolean
+ // Require explicit assignment of DontCare to generate "x is invalid"
+ val explicitInvalidate: Boolean
}
object CompileOptions {
@@ -44,7 +46,9 @@ object ExplicitCompileOptions {
// If connection directionality is not explicit, do not use heuristics to attempt to determine it.
val dontAssumeDirectionality: Boolean,
// Check that referenced Data have actually been declared.
- val checkSynthesizable: Boolean
+ val checkSynthesizable: Boolean,
+ // Require an explicit DontCare assignment to generate a firrtl DefInvalid
+ val explicitInvalidate: Boolean
) extends CompileOptions
// Collection of "not strict" connection compile options.
@@ -55,7 +59,8 @@ object ExplicitCompileOptions {
declaredTypeMustBeUnbound = false,
dontTryConnectionsSwapped = false,
dontAssumeDirectionality = false,
- checkSynthesizable = false
+ checkSynthesizable = false,
+ explicitInvalidate = false
)
// Collection of "strict" connection compile options, preferred for new code.
@@ -65,6 +70,7 @@ object ExplicitCompileOptions {
declaredTypeMustBeUnbound = true,
dontTryConnectionsSwapped = true,
dontAssumeDirectionality = true,
- checkSynthesizable = true
+ checkSynthesizable = true,
+ explicitInvalidate = true
)
}
diff --git a/chiselFrontend/src/main/scala/chisel3/core/Data.scala b/chiselFrontend/src/main/scala/chisel3/core/Data.scala
index aa286e0d..db99d6b3 100644
--- a/chiselFrontend/src/main/scala/chisel3/core/Data.scala
+++ b/chiselFrontend/src/main/scala/chisel3/core/Data.scala
@@ -8,6 +8,7 @@ import chisel3.internal._
import chisel3.internal.Builder.{pushCommand, pushOp}
import chisel3.internal.firrtl._
import chisel3.internal.sourceinfo._
+import chisel3.core.BiConnect.DontCareCantBeSink
/** User-specified directions.
*/
@@ -33,8 +34,8 @@ object SpecifiedDirection {
case Input => Output
}
- /** Returns the effective UserDirection of this node given the parent's effective UserDirection
- * and the user-specified UserDirection of this node.
+ /** Returns the effective SpecifiedDirection of this node given the parent's effective SpecifiedDirection
+ * and the user-specified SpecifiedDirection of this node.
*/
def fromParent(parentDirection: SpecifiedDirection, thisDirection: SpecifiedDirection) =
(parentDirection, thisDirection) match {
@@ -222,7 +223,7 @@ abstract class Data extends HasId {
_specifiedDirection = direction
}
- /** This overwrites a relative UserDirection with an explicit one, and is used to implement
+ /** This overwrites a relative SpecifiedDirection with an explicit one, and is used to implement
* the compatibility layer where, at the elements, Flip is Input and unspecified is Output.
* DO NOT USE OUTSIDE THIS PURPOSE. THIS OPERATION IS DANGEROUS!
*/
@@ -311,6 +312,8 @@ abstract class Data extends HasId {
requireIsHardware(that, s"data to be bulk-connected")
(this.topBinding, that.topBinding) match {
case (_: ReadOnlyBinding, _: ReadOnlyBinding) => throwException(s"Both $this and $that are read-only")
+ // DontCare cannot be a sink (LHS)
+ case (_: DontCareBinding, _) => throw DontCareCantBeSink
case _ => // fine
}
try {
@@ -425,7 +428,9 @@ trait WireFactory {
x.bind(WireBinding(Builder.forcedUserModule))
pushCommand(DefWire(sourceInfo, x))
- pushCommand(DefInvalid(sourceInfo, x.ref))
+ if (!compileOptions.explicitInvalidate) {
+ pushCommand(DefInvalid(sourceInfo, x.ref))
+ }
x
}
@@ -454,3 +459,27 @@ object WireInit {
x
}
}
+
+/** RHS (source) for Invalidate API.
+ * Causes connection logic to emit a DefInvalid when connected to an output port (or wire).
+ */
+object DontCare extends Element(width = UnknownWidth()) {
+ // This object should be initialized before we execute any user code that refers to it,
+ // otherwise this "Chisel" object will end up on the UserModule's id list.
+
+ bind(DontCareBinding(), SpecifiedDirection.Output)
+ override def cloneType = DontCare
+
+ def toPrintable: Printable = PString("DONTCARE")
+
+ private[core] def connectFromBits(that: chisel3.core.Bits)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Unit = {
+ Builder.error("connectFromBits: DontCare cannot be a connection sink (LHS)")
+ }
+
+ def do_asUInt(implicit sourceInfo: chisel3.internal.sourceinfo.SourceInfo, compileOptions: CompileOptions): chisel3.core.UInt = {
+ Builder.error("DontCare does not have a UInt representation")
+ 0.U
+ }
+ // DontCare's only match themselves.
+ private[core] def typeEquivalent(that: chisel3.core.Data): Boolean = that == DontCare
+}
diff --git a/chiselFrontend/src/main/scala/chisel3/core/MonoConnect.scala b/chiselFrontend/src/main/scala/chisel3/core/MonoConnect.scala
index 3c34785f..c4e880b7 100644
--- a/chiselFrontend/src/main/scala/chisel3/core/MonoConnect.scala
+++ b/chiselFrontend/src/main/scala/chisel3/core/MonoConnect.scala
@@ -3,7 +3,7 @@
package chisel3.core
import chisel3.internal.Builder.pushCommand
-import chisel3.internal.firrtl.Connect
+import chisel3.internal.firrtl.{Connect, DefInvalid}
import scala.language.experimental.macros
import chisel3.internal.sourceinfo.SourceInfo
@@ -48,6 +48,8 @@ object MonoConnect {
MonoConnectException(s": Source Record missing field ($field).")
def MismatchedException(sink: String, source: String) =
MonoConnectException(s": Sink ($sink) and Source ($source) have different types.")
+ def DontCareCantBeSink =
+ MonoConnectException(": DontCare cannot be a connection sink (LHS)")
/** This function is what recursively tries to connect a sink and source together
*
@@ -89,6 +91,16 @@ object MonoConnect {
case MonoConnectException(message) => throw MonoConnectException(s"($idx)$message")
}
}
+ // Handle Vec connected to DontCare. Apply the DontCare to individual elements.
+ case (sink_v: Vec[Data @unchecked], DontCare) =>
+ for(idx <- 0 until sink_v.length) {
+ try {
+ implicit val compileOptions = connectCompileOptions
+ connect(sourceInfo, connectCompileOptions, sink_v(idx), source, context_mod)
+ } catch {
+ case MonoConnectException(message) => throw MonoConnectException(s"($idx)$message")
+ }
+ }
// Handle Record case
case (sink_r: Record, source_r: Record) =>
@@ -107,14 +119,33 @@ object MonoConnect {
case MonoConnectException(message) => throw MonoConnectException(s".$field$message")
}
}
+ // Handle Record connected to DontCare. Apply the DontCare to individual elements.
+ case (sink_r: Record, DontCare) =>
+ // For each field, descend with right
+ for((field, sink_sub) <- sink_r.elements) {
+ try {
+ connect(sourceInfo, connectCompileOptions, sink_sub, source, context_mod)
+ } catch {
+ case MonoConnectException(message) => throw MonoConnectException(s".$field$message")
+ }
+ }
+ // Source is DontCare - it may be connected to anything. It generates a defInvalid for the sink.
+ case (sink, DontCare) => pushCommand(DefInvalid(sourceInfo, sink.lref))
+ // DontCare as a sink is illegal.
+ case (DontCare, _) => throw DontCareCantBeSink
// Sink and source are different subtypes of data so fail
case (sink, source) => throw MismatchedException(sink.toString, source.toString)
}
// This function (finally) issues the connection operation
private def issueConnect(sink: Element, source: Element)(implicit sourceInfo: SourceInfo): Unit = {
- pushCommand(Connect(sourceInfo, sink.lref, source.ref))
+ // If the source is a DontCare, generate a DefInvalid for the sink,
+ // otherwise, issue a Connect.
+ source.binding match {
+ case b: DontCareBinding => pushCommand(DefInvalid(sourceInfo, sink.lref))
+ case _ => pushCommand(Connect(sourceInfo, sink.lref, source.ref))
+ }
}
// This function checks if element-level connection operation allowed.
diff --git a/chiselFrontend/src/main/scala/chisel3/core/UserModule.scala b/chiselFrontend/src/main/scala/chisel3/core/UserModule.scala
index 218b27c6..c99d53cf 100644
--- a/chiselFrontend/src/main/scala/chisel3/core/UserModule.scala
+++ b/chiselFrontend/src/main/scala/chisel3/core/UserModule.scala
@@ -67,9 +67,15 @@ abstract class UserModule(implicit moduleCompileOptions: CompileOptions)
val firrtlPorts = getModulePorts map {port => Port(port, port.specifiedDirection)}
_firrtlPorts = Some(firrtlPorts)
- // Generate IO invalidation commands to initialize outputs as unused
- val invalidateCommands = getModulePorts map {port => DefInvalid(UnlocatableSourceInfo, port.ref)}
-
+ // Generate IO invalidation commands to initialize outputs as unused,
+ // unless the client wants explicit control over their generation.
+ val invalidateCommands = {
+ if (!compileOptions.explicitInvalidate) {
+ getModulePorts map { port => DefInvalid(UnlocatableSourceInfo, port.ref) }
+ } else {
+ Seq()
+ }
+ }
val component = DefModule(this, name, firrtlPorts, invalidateCommands ++ getCommands)
_component = Some(component)
component
@@ -97,10 +103,11 @@ abstract class ImplicitModule(implicit moduleCompileOptions: CompileOptions)
private[core] override def initializeInParent() {
implicit val sourceInfo = UnlocatableSourceInfo
- for (port <- getModulePorts) {
- pushCommand(DefInvalid(sourceInfo, port.ref))
+ if (!compileOptions.explicitInvalidate) {
+ for (port <- getModulePorts) {
+ pushCommand(DefInvalid(sourceInfo, port.ref))
+ }
}
-
clock := Builder.forcedClock
reset := Builder.forcedReset
}
@@ -171,7 +178,9 @@ abstract class LegacyModule(implicit moduleCompileOptions: CompileOptions)
// module de-duplication in FIRRTL emission.
implicit val sourceInfo = UnlocatableSourceInfo
- pushCommand(DefInvalid(sourceInfo, io.ref))
+ if (!compileOptions.explicitInvalidate) {
+ pushCommand(DefInvalid(sourceInfo, io.ref))
+ }
override_clock match {
case Some(override_clock) => clock := override_clock
diff --git a/chiselFrontend/src/main/scala/chisel3/internal/Builder.scala b/chiselFrontend/src/main/scala/chisel3/internal/Builder.scala
index 8af1835d..90eabba3 100644
--- a/chiselFrontend/src/main/scala/chisel3/internal/Builder.scala
+++ b/chiselFrontend/src/main/scala/chisel3/internal/Builder.scala
@@ -164,6 +164,10 @@ private[chisel3] object Builder {
private def dynamicContext: DynamicContext =
dynamicContextVar.value.getOrElse(new DynamicContext)
+ // Initialize any singleton objects before user code inadvertently inherits them.
+ private def initializeSingletons(): Unit = {
+ val dummy = DontCare
+ }
def idGen: IdGen = dynamicContext.idGen
def globalNamespace: Namespace = dynamicContext.globalNamespace
def components: ArrayBuffer[Component] = dynamicContext.components
@@ -245,6 +249,7 @@ private[chisel3] object Builder {
Circuit(components.last.name, components, annotations.map(_.toFirrtl))
}
}
+ initializeSingletons()
}
/** Allows public access to the naming stack in Builder / DynamicContext.
diff --git a/src/main/scala/chisel3/internal/firrtl/Emitter.scala b/src/main/scala/chisel3/internal/firrtl/Emitter.scala
index 09984722..26ccc09d 100644
--- a/src/main/scala/chisel3/internal/firrtl/Emitter.scala
+++ b/src/main/scala/chisel3/internal/firrtl/Emitter.scala
@@ -159,10 +159,16 @@ private class Emitter(circuit: Circuit) {
* alternative-free statements reset the indent level to the
* enclosing block upon emission.
*/
- private def processWhens(cmds: Seq[Command]):
- Seq[Command] = { cmds.zip(cmds.tail).map({ case (a: WhenEnd, b:
- AltBegin) => a.copy(hasAlt = true) case (a, b) => a }) ++
- cmds.lastOption }
+ private def processWhens(cmds: Seq[Command]): Seq[Command] = {
+ if (cmds.isEmpty) {
+ Seq.empty
+ } else {
+ cmds.zip(cmds.tail).map{
+ case (a: WhenEnd, b: AltBegin) => a.copy(hasAlt = true)
+ case (a, b) => a
+ } ++ cmds.lastOption
+ }
+ }
private var indentLevel = 0
private def newline = "\n" + (" " * indentLevel)
diff --git a/src/main/scala/chisel3/package.scala b/src/main/scala/chisel3/package.scala
index d335f1f1..f31b4015 100644
--- a/src/main/scala/chisel3/package.scala
+++ b/src/main/scala/chisel3/package.scala
@@ -349,6 +349,9 @@ package object chisel3 { // scalastyle:ignore package.object.name
a.allElements
}
def getModulePorts(m: Module): Seq[Port] = m.getPorts
+ // Invalidate API - a DontCare element for explicit assignment to outputs,
+ // indicating the signal is intentionally not driven.
+ val DontCare = chisel3.core.DontCare
/** Package for experimental features, which may have their API changed, be removed, etc.
*
diff --git a/src/main/scala/chisel3/util/Decoupled.scala b/src/main/scala/chisel3/util/Decoupled.scala
index 451fd039..d35046af 100644
--- a/src/main/scala/chisel3/util/Decoupled.scala
+++ b/src/main/scala/chisel3/util/Decoupled.scala
@@ -44,8 +44,6 @@ object ReadyValidIO {
*/
def noenq(): Unit = {
target.valid := false.B
- // We want the type from the following, not any existing binding.
- target.bits := Wire(target.bits.cloneType)
}
/** Assert ready on this port and return the associated data bits.
diff --git a/src/test/scala/chiselTests/ComplexAssign.scala b/src/test/scala/chiselTests/ComplexAssign.scala
index a13ec959..e101e7c0 100644
--- a/src/test/scala/chiselTests/ComplexAssign.scala
+++ b/src/test/scala/chiselTests/ComplexAssign.scala
@@ -8,6 +8,7 @@ import org.scalatest.prop._
import chisel3._
import chisel3.testers.BasicTester
import chisel3.util._
+import org.scalacheck.Shrink
class Complex[T <: Data](val re: T, val im: T) extends Bundle {
override def cloneType: this.type =
@@ -47,6 +48,9 @@ class ComplexAssignTester(enList: List[Boolean], re: Int, im: Int) extends Basic
class ComplexAssignSpec extends ChiselPropSpec {
property("All complex assignments should return the correct result") {
+ // Disable shrinking on error.
+ implicit val noShrinkListVal = Shrink[List[Boolean]](_ => Stream.empty)
+ implicit val noShrinkInt = Shrink[Int](_ => Stream.empty)
forAll(enSequence(2), safeUInts, safeUInts) { (en: List[Boolean], re: Int, im: Int) =>
assertTesterPasses{ new ComplexAssignTester(en, re, im) }
}
diff --git a/src/test/scala/chiselTests/DontTouchSpec.scala b/src/test/scala/chiselTests/DontTouchSpec.scala
index 9a4e6660..7aa4d2e2 100644
--- a/src/test/scala/chiselTests/DontTouchSpec.scala
+++ b/src/test/scala/chiselTests/DontTouchSpec.scala
@@ -13,6 +13,7 @@ class HasDeadCodeChild(withDontTouch: Boolean) extends Module {
val c = Output(Vec(2, UInt(32.W)))
})
io.b := io.a
+ io.c := DontCare
if (withDontTouch) {
dontTouch(io.c)
}
diff --git a/src/test/scala/chiselTests/FixedPointSpec.scala b/src/test/scala/chiselTests/FixedPointSpec.scala
index 28c3aa55..8caa7f1e 100644
--- a/src/test/scala/chiselTests/FixedPointSpec.scala
+++ b/src/test/scala/chiselTests/FixedPointSpec.scala
@@ -108,6 +108,8 @@ class SBPTester extends BasicTester {
assert(dut.io.out === 3.0.F(0.BP))
val test = Wire(FixedPoint(10.W, 5.BP))
+ // Initialize test, avoiding a "Reference test is not fully initialized" error from firrtl.
+ test := 0.0.F(5.BP)
val q = test.setBinaryPoint(18)
assert(q.getWidth.U === 23.U)
diff --git a/src/test/scala/chiselTests/InvalidateAPISpec.scala b/src/test/scala/chiselTests/InvalidateAPISpec.scala
new file mode 100644
index 00000000..7176c654
--- /dev/null
+++ b/src/test/scala/chiselTests/InvalidateAPISpec.scala
@@ -0,0 +1,164 @@
+// See LICENSE for license details.
+
+package chiselTests
+
+import chisel3._
+import chisel3.core.BiConnect.BiConnectException
+import chisel3.util.Counter
+import firrtl.passes.CheckInitialization.RefNotInitializedException
+import org.scalatest._
+
+class InvalidateAPISpec extends ChiselPropSpec with Matchers {
+
+ def myGenerateFirrtl(t: => Module): String = Driver.emit(() => t)
+ def compileFirrtl(t: => Module): Unit = {
+ Driver.execute(Array[String]("--compiler", "verilog"), () => t)
+ }
+ class TrivialInterface extends Bundle {
+ val in = Input(Bool())
+ val out = Output(Bool())
+ }
+
+ property("an output connected to DontCare should emit a Firrtl \"is invalid\" with Strict CompileOptions") {
+ import chisel3.core.ExplicitCompileOptions.Strict
+ class ModuleWithDontCare extends Module {
+ val io = IO(new TrivialInterface)
+ io.out := DontCare
+ io.out := io.in
+ }
+ val firrtlOutput = myGenerateFirrtl(new ModuleWithDontCare)
+ firrtlOutput should include("io.out is invalid")
+ }
+
+ property("an output without a DontCare should NOT emit a Firrtl \"is invalid\" with Strict CompileOptions") {
+ import chisel3.core.ExplicitCompileOptions.Strict
+ class ModuleWithoutDontCare extends Module {
+ val io = IO(new TrivialInterface)
+ io.out := io.in
+ }
+ val firrtlOutput = myGenerateFirrtl(new ModuleWithoutDontCare)
+ firrtlOutput should not include("is invalid")
+ }
+
+ property("an output without a DontCare should emit a Firrtl \"is invalid\" with NotStrict CompileOptions") {
+ import chisel3.core.ExplicitCompileOptions.NotStrict
+ class ModuleWithoutDontCare extends Module {
+ val io = IO(new TrivialInterface)
+ io.out := io.in
+ }
+ val firrtlOutput = myGenerateFirrtl(new ModuleWithoutDontCare)
+ firrtlOutput should include("io is invalid")
+ }
+
+ property("a bundle with a DontCare should emit a Firrtl \"is invalid\" with Strict CompileOptions") {
+ import chisel3.core.ExplicitCompileOptions.Strict
+ class ModuleWithoutDontCare extends Module {
+ val io = IO(new TrivialInterface)
+ io <> DontCare
+ }
+ val firrtlOutput = myGenerateFirrtl(new ModuleWithoutDontCare)
+ firrtlOutput should include("io.out is invalid")
+ firrtlOutput should include("io.in is invalid")
+ }
+
+ property("a Vec with a DontCare should emit a Firrtl \"is invalid\" with Strict CompileOptions and bulk connect") {
+ import chisel3.core.ExplicitCompileOptions.Strict
+ val nElements = 5
+ class ModuleWithoutDontCare extends Module {
+ val io = IO(new Bundle {
+ val ins = Input(Vec(nElements, Bool()))
+ })
+ io.ins <> DontCare
+ }
+ val firrtlOutput = myGenerateFirrtl(new ModuleWithoutDontCare)
+ for (i <- 0 until nElements)
+ firrtlOutput should include(s"io.ins[$i] is invalid")
+ }
+
+ property("a Vec with a DontCare should emit a Firrtl \"is invalid\" with Strict CompileOptions and mono connect") {
+ import chisel3.core.ExplicitCompileOptions.Strict
+ val nElements = 5
+ class ModuleWithoutDontCare extends Module {
+ val io = IO(new Bundle {
+ val ins = Input(Vec(nElements, Bool()))
+ })
+ io.ins := DontCare
+ }
+ val firrtlOutput = myGenerateFirrtl(new ModuleWithoutDontCare)
+ for (i <- 0 until nElements)
+ firrtlOutput should include(s"io.ins[$i] is invalid")
+ }
+
+ property("a DontCare cannot be a connection sink (LHS) for := ") {
+ import chisel3.core.ExplicitCompileOptions.Strict
+ class ModuleWithDontCareSink extends Module {
+ val io = IO(new TrivialInterface)
+ DontCare := io.in
+ }
+ val exception = intercept[ChiselException] {
+ elaborate(new ModuleWithDontCareSink)
+ }
+ exception.getMessage should include("DontCare cannot be a connection sink (LHS)")
+ }
+
+ property("a DontCare cannot be a connection sink (LHS) for <>") {
+ import chisel3.core.ExplicitCompileOptions.Strict
+ class ModuleWithDontCareSink extends Module {
+ val io = IO(new TrivialInterface)
+ DontCare <> io.in
+ }
+ val exception = intercept[BiConnectException] {
+ elaborate(new ModuleWithDontCareSink)
+ }
+ exception.getMessage should include("DontCare cannot be a connection sink (LHS)")
+ }
+
+ property("FIRRTL should complain about partial initialization with Strict CompileOptions and conditional connect") {
+ import chisel3.core.ExplicitCompileOptions.Strict
+ class ModuleWithIncompleteAssignment extends Module {
+ val io = IO(new Bundle {
+ val out = Output(Bool())
+ })
+ val counter = Counter(8)
+ when (counter.inc()) {
+ io.out := true.B
+ }
+ }
+ val exception = intercept[RefNotInitializedException] {
+ compileFirrtl(new ModuleWithIncompleteAssignment)
+ }
+ exception.getMessage should include("is not fully initialized")
+ }
+
+ property("FIRRTL should not complain about partial initialization with Strict CompileOptions and conditional connect after unconditional connect") {
+ import chisel3.core.ExplicitCompileOptions.Strict
+ class ModuleWithUnconditionalAssignment extends Module {
+ val io = IO(new Bundle {
+ val out = Output(Bool())
+ })
+ val counter = Counter(8)
+ io.out := false.B
+ when (counter.inc()) {
+ io.out := true.B
+ }
+ }
+ compileFirrtl(new ModuleWithUnconditionalAssignment)
+ }
+
+ property("FIRRTL should not complain about partial initialization with Strict CompileOptions and conditional connect with otherwise clause") {
+ import chisel3.core.ExplicitCompileOptions.Strict
+ class ModuleWithConditionalAndOtherwiseAssignment extends Module {
+ val io = IO(new Bundle {
+ val out = Output(Bool())
+ })
+ val counter = Counter(8)
+ when (counter.inc()) {
+ io.out := true.B
+ } otherwise {
+ io.out := false.B
+ }
+ }
+
+ compileFirrtl(new ModuleWithConditionalAndOtherwiseAssignment)
+ }
+}
diff --git a/src/test/scala/chiselTests/Vec.scala b/src/test/scala/chiselTests/Vec.scala
index 6c62ab26..1c5157b5 100644
--- a/src/test/scala/chiselTests/Vec.scala
+++ b/src/test/scala/chiselTests/Vec.scala
@@ -146,6 +146,7 @@ class ZeroEntryVecTester extends BasicTester {
val m = Module(new Module {
val io = IO(Output(bundleWithZeroEntryVec))
+ io.foo := false.B
})
WireInit(m.io.bar)
@@ -179,6 +180,8 @@ class ModuleIODynamicIndexTester(n: Int) extends BasicTester {
when (cycle =/= i.U) {
m.in := 0.U // default
assert(m.out === 0.U)
+ } .otherwise {
+ m.in := DontCare
}
}
// only connect one dut per cycle
diff --git a/src/test/scala/chiselTests/VectorPacketIO.scala b/src/test/scala/chiselTests/VectorPacketIO.scala
index bcf59e03..ba3664a3 100644
--- a/src/test/scala/chiselTests/VectorPacketIO.scala
+++ b/src/test/scala/chiselTests/VectorPacketIO.scala
@@ -40,6 +40,11 @@ class BrokenVectorPacketModule extends Module {
val n = 4
val io = IO(new VectorPacketIO(n))
+ // Avoid a "Reference io is not fully initialized" error from firrtl.
+ for (i <- 0 until n) {
+ io.outs(i) <> io.ins(i)
+ }
+
/* the following method of initializing the circuit may change in the future */
io.ins.foreach(_.nodeq())
io.outs.foreach(_.noenq())
diff --git a/src/test/scala/chiselTests/When.scala b/src/test/scala/chiselTests/When.scala
index d2646df7..3bd63831 100644
--- a/src/test/scala/chiselTests/When.scala
+++ b/src/test/scala/chiselTests/When.scala
@@ -7,7 +7,6 @@ import org.scalatest._
import chisel3._
import chisel3.testers.BasicTester
import chisel3.util._
-//import chisel3.core.ExplicitCompileOptions.Strict
class WhenTester() extends BasicTester {
val cnt = Counter(4)
@@ -66,6 +65,8 @@ class NoOtherwiseOverlappedWhenTester() extends BasicTester {
out := 3.U
} .elsewhen (cnt.value <= 3.U) {
out := 0.U
+ } .otherwise {
+ out := DontCare
}
assert(out === cnt.value + 1.U)