From aaee58d374dc3f3a088856da13a6a59ecffb2cac Mon Sep 17 00:00:00 2001 From: Richard Lin Date: Mon, 26 Jun 2017 18:35:46 -0700 Subject: Directions internals mega-refactor (#617) Part 1 of mega-change in #578 Major notes: - Input(...) and Output(...) now (effectively) recursively override their elements' directions - Nodes given userDirection (Input, Output, Flip - what the user assigned to _that_ node) and actualDirection (Input, Output, None, but also Bidirectional and BidirectionalFlip for mostly Aggregates), because of the above (since a higher-level Input(...) can override the locally specified user direction). - DataMirror (node reflection APIs) added to chisel3.experimental. This provides ways to query the user given direction of a node as well as the actual direction. - checkSynthesizable replaced with requireIsHardware and requireIsChiselType and made available in chisel3.experimental. Internal changes notes: - toType moved into Emitter, this makes the implementation cleaner especially considering that Vec types can't be flipped in FIRRTL. This also more clearly separates Chisel frontend from FIRRTL emission. - Direction separated from Bindings, both are now fields in Data, and all nodes are given hierarchical directions (Aggregates may be Bidirectional). The actualDirection at the Element (leaf) level should be the same as binding directions previously. - Bindings are hierarchical, children (of a, for example, Bundle) have a ChildBinding that points to their parent. This is different than the previous scheme where Bindings only applied at the Element (leaf) level. - Lots of small misc clean up. Future PRs will address other parts of #578, including stricter direction checks that aren't a side-effect of this internal refactor, stricter checks and splitting of binding operations (Wire vs. WireInit), and node operations not introduced here (getType and deprecation of chiselCloneType). Since those shouldn't mess with internals, those should be much smaller.--- .../src/main/scala/chisel3/core/Aggregate.scala | 111 +++++--- .../src/main/scala/chisel3/core/BiConnect.scala | 91 +++--- .../src/main/scala/chisel3/core/Binder.scala | 64 ----- .../src/main/scala/chisel3/core/Binding.scala | 239 +++++----------- .../src/main/scala/chisel3/core/Bits.scala | 82 +++--- .../src/main/scala/chisel3/core/BlackBox.scala | 20 +- .../main/scala/chisel3/core/ChiselAnnotation.scala | 4 +- .../src/main/scala/chisel3/core/Clock.scala | 34 +++ .../main/scala/chisel3/core/CompileOptions.scala | 8 - .../src/main/scala/chisel3/core/Data.scala | 306 +++++++++++---------- .../src/main/scala/chisel3/core/Mem.scala | 9 +- .../src/main/scala/chisel3/core/Module.scala | 28 +- .../src/main/scala/chisel3/core/MonoConnect.scala | 43 ++- .../src/main/scala/chisel3/core/Mux.scala | 8 +- .../src/main/scala/chisel3/core/Reg.scala | 12 +- .../src/main/scala/chisel3/core/UserModule.scala | 18 +- .../src/main/scala/chisel3/internal/Builder.scala | 2 +- .../main/scala/chisel3/internal/firrtl/IR.scala | 2 +- src/main/scala/chisel3/compatibility.scala | 71 +++-- .../scala/chisel3/internal/firrtl/Emitter.scala | 49 +++- src/main/scala/chisel3/package.scala | 30 +- src/main/scala/chisel3/util/Decoupled.scala | 5 +- src/test/scala/chiselTests/BlackBox.scala | 2 +- .../CompatibilityInteroperabilitySpec.scala | 32 +-- .../scala/chiselTests/CompileOptionsTest.scala | 8 +- src/test/scala/chiselTests/Direction.scala | 16 ++ src/test/scala/chiselTests/FromBitsTester.scala | 3 +- .../MissingCloneBindingExceptionSpec.scala | 2 +- src/test/scala/chiselTests/Module.scala | 2 +- src/test/scala/chiselTests/RecordSpec.scala | 3 +- src/test/scala/chiselTests/ReinterpretCast.scala | 3 +- 31 files changed, 644 insertions(+), 663 deletions(-) delete mode 100644 chiselFrontend/src/main/scala/chisel3/core/Binder.scala create mode 100644 chiselFrontend/src/main/scala/chisel3/core/Clock.scala diff --git a/chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala b/chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala index 3f81de9f..701cf892 100644 --- a/chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala +++ b/chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala @@ -15,11 +15,58 @@ import chisel3.internal.sourceinfo._ * of) other Data objects. */ sealed abstract class Aggregate extends Data { + private[chisel3] override def bind(target: Binding, parentDirection: UserDirection) { + binding = target + + val resolvedDirection = UserDirection.fromParent(parentDirection, userDirection) + for (child <- getElements) { + child.bind(ChildBinding(this), resolvedDirection) + } + + // Check that children obey the directionality rules. + val childDirections = getElements.map(_.direction).toSet + direction = if (childDirections == Set()) { // Sadly, Scala can't do set matching + // If empty, use my assigned direction + resolvedDirection match { + case UserDirection.Unspecified | UserDirection.Flip => ActualDirection.Unspecified + case UserDirection.Output => ActualDirection.Output + case UserDirection.Input => ActualDirection.Input + } + } else if (childDirections == Set(ActualDirection.Unspecified)) { + ActualDirection.Unspecified + } else if (childDirections == Set(ActualDirection.Input)) { + ActualDirection.Input + } else if (childDirections == Set(ActualDirection.Output)) { + ActualDirection.Output + } else if (childDirections subsetOf + Set(ActualDirection.Output, ActualDirection.Input, + ActualDirection.Bidirectional(ActualDirection.Default), + ActualDirection.Bidirectional(ActualDirection.Flipped))) { + resolvedDirection match { + case UserDirection.Unspecified => ActualDirection.Bidirectional(ActualDirection.Default) + case UserDirection.Flip => ActualDirection.Bidirectional(ActualDirection.Flipped) + case _ => throw new RuntimeException("Unexpected forced Input / Output") + } + } else { + this match { + // Anything flies in compatibility mode + case t: Record if !t.compileOptions.dontAssumeDirectionality => resolvedDirection match { + case UserDirection.Unspecified => ActualDirection.Bidirectional(ActualDirection.Default) + case UserDirection.Flip => ActualDirection.Bidirectional(ActualDirection.Flipped) + case _ => ActualDirection.Bidirectional(ActualDirection.Default) + } + case _ => + val childWithDirections = getElements zip getElements.map(_.direction) + throw Binding.MixedDirectionAggregateException(s"Aggregate '$this' can't have elements that are both directioned and undirectioned: $childWithDirections") + } + } + } + /** Returns a Seq of the immediate contents of this Aggregate, in order. */ def getElements: Seq[Data] - private[core] def width: Width = getElements.map(_.width).foldLeft(0.W)(_ + _) + 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)) @@ -68,25 +115,21 @@ object Vec { // Check that types are homogeneous. Width mismatch for Elements is safe. require(!elts.isEmpty) + elts.foreach(requireIsHardware(_, "vec element")) val vec = Wire(new Vec(cloneSupertype(elts, "Vec"), elts.length)) - def doConnect(sink: T, source: T) = { - // TODO: this looks bad, and should feel bad. Replace with a better abstraction. - // NOTE: Must use elts.head instead of vec.sample_element because vec.sample_element has - // WireBinding which does not have a direction - val hasDirectioned = elts.head match { - case t: Aggregate => t.flatten.exists(_.dir != Direction.Unspecified) - case t: Element => t.dir != Direction.Unspecified - } - if (hasDirectioned) { - sink bulkConnect source - } else { - sink connect source - } - } - for ((v, e) <- vec zip elts) { - doConnect(v, e) + // TODO: try to remove the logic for this mess + elts.head.direction match { + case ActualDirection.Input | ActualDirection.Output | ActualDirection.Unspecified => + // When internal wires are involved, driver / sink must be specified explicitly, otherwise + // the system is unable to infer which is driver / sink + (vec zip elts).foreach(x => x._1 := x._2) + case ActualDirection.Bidirectional(_) => + // For bidirectional, must issue a bulk connect so subelements are resolved correctly. + // Bulk connecting two wires may not succeed because Chisel frontend does not infer + // directions. + (vec zip elts).foreach(x => x._1 <> x._2) } vec } @@ -186,7 +229,7 @@ sealed class Vec[T <: Data] private (gen: => T, val length: Int) * * Needed specifically for the case when the Vec is length 0. */ - private[core] val sample_element: T = gen + private[chisel3] val sample_element: T = gen // allElements current includes sample_element // This is somewhat weird although I think the best course of action here is @@ -226,16 +269,25 @@ sealed class Vec[T <: Data] private (gen: => T, val length: Int) override def apply(p: UInt): T = macro CompileOptionsTransform.pArg def do_apply(p: UInt)(implicit compileOptions: CompileOptions): T = { - Binding.checkSynthesizable(p ,s"'p' ($p)") + requireIsHardware(p, "vec index") val port = gen - val i = Vec.truncateIndex(p, length)(UnlocatableSourceInfo, compileOptions) - port.setRef(this, i) - // Bind each element of port to being whatever the base type is - // Using the head element as the sample_element - for((port_elem, model_elem) <- port.allElements zip sample_element.allElements) { - port_elem.binding = model_elem.binding + // Reconstruct the resolvedDirection (in Aggregate.bind), since it's not stored. + // It may not be exactly equal to that value, but the results are the same. + val reconstructedResolvedDirection = direction match { + case ActualDirection.Input => UserDirection.Input + case ActualDirection.Output => UserDirection.Output + case ActualDirection.Bidirectional(ActualDirection.Default) | ActualDirection.Unspecified => + UserDirection.Unspecified + case ActualDirection.Bidirectional(ActualDirection.Flipped) => UserDirection.Flip } + // TODO port technically isn't directly child of this data structure, but the result of some + // muxes / demuxes. However, this does make access consistent with the top-level bindings. + // Perhaps there's a cleaner way of accomplishing this... + port.bind(ChildBinding(this), reconstructedResolvedDirection) + + val i = Vec.truncateIndex(p, length)(UnlocatableSourceInfo, compileOptions) + port.setRef(this, i) port } @@ -256,7 +308,6 @@ sealed class Vec[T <: Data] private (gen: => T, val length: Int) new Vec(gen.cloneType, length).asInstanceOf[this.type] } - private[chisel3] def toType: String = s"${sample_element.toType}[$length]" override def getElements: Seq[Data] = (0 until length).map(apply(_)) @@ -384,14 +435,6 @@ abstract class Record(private[chisel3] implicit val compileOptions: CompileOptio /** Name for Pretty Printing */ def className: String = this.getClass.getSimpleName - private[chisel3] def toType = { - def eltPort(elt: Data): String = { - val flipStr: String = if(Data.isFirrtlFlipped(elt)) "flip " else "" - s"${flipStr}${elt.getRef.name} : ${elt.toType}" - } - elements.toIndexedSeq.reverse.map(e => eltPort(e._2)).mkString("{", ", ", "}") - } - private[core] override def typeEquivalent(that: Data): Boolean = that match { case that: Record => this.getClass == that.getClass && diff --git a/chiselFrontend/src/main/scala/chisel3/core/BiConnect.scala b/chiselFrontend/src/main/scala/chisel3/core/BiConnect.scala index 0e5d4e8b..f2b2b7e1 100644 --- a/chiselFrontend/src/main/scala/chisel3/core/BiConnect.scala +++ b/chiselFrontend/src/main/scala/chisel3/core/BiConnect.scala @@ -138,15 +138,14 @@ object BiConnect { // This function checks if element-level connection operation allowed. // Then it either issues it or throws the appropriate exception. def elemConnect(implicit sourceInfo: SourceInfo, connectCompileOptions: CompileOptions, left: Element, right: Element, context_mod: UserModule): Unit = { - import Direction.{Input, Output} // Using extensively so import these + import BindingDirection.{Internal, Input, Output} // Using extensively so import these // If left or right have no location, assume in context module // This can occur if one of them is a literal, unbound will error previously - val left_mod: BaseModule = left.binding.location.getOrElse(context_mod) - val right_mod: BaseModule = right.binding.location.getOrElse(context_mod) + val left_mod: BaseModule = left.topBinding.location.getOrElse(context_mod) + val right_mod: BaseModule = right.topBinding.location.getOrElse(context_mod) - val left_direction: Option[Direction] = left.binding.direction - val right_direction: Option[Direction] = right.binding.direction - // None means internal + val left_direction = BindingDirection.from(left.topBinding, left.direction) + val right_direction = BindingDirection.from(right.topBinding, right.direction) // CASE: Context is same module as left node and right node is in a child module if( (left_mod == context_mod) && @@ -154,15 +153,15 @@ object BiConnect { // Thus, right node better be a port node and thus have a direction hint ((left_direction, right_direction): @unchecked) match { // CURRENT MOD CHILD MOD - case (Some(Input), Some(Input)) => issueConnectL2R(left, right) - case (None, Some(Input)) => issueConnectL2R(left, right) + case (Input, Input) => issueConnectL2R(left, right) + case (Internal, Input) => issueConnectL2R(left, right) - case (Some(Output), Some(Output)) => issueConnectR2L(left, right) - case (None, Some(Output)) => issueConnectR2L(left, right) + case (Output, Output) => issueConnectR2L(left, right) + case (Internal, Output) => issueConnectR2L(left, right) - case (Some(Input), Some(Output)) => throw BothDriversException - case (Some(Output), Some(Input)) => throw NeitherDriverException - case (_, None) => throw UnknownRelationException + case (Input, Output) => throw BothDriversException + case (Output, Input) => throw NeitherDriverException + case (_, Internal) => throw UnknownRelationException } } @@ -172,15 +171,15 @@ object BiConnect { // Thus, left node better be a port node and thus have a direction hint ((left_direction, right_direction): @unchecked) match { // CHILD MOD CURRENT MOD - case (Some(Input), Some(Input)) => issueConnectR2L(left, right) - case (Some(Input), None) => issueConnectR2L(left, right) + case (Input, Input) => issueConnectR2L(left, right) + case (Input, Internal) => issueConnectR2L(left, right) - case (Some(Output), Some(Output)) => issueConnectL2R(left, right) - case (Some(Output), None) => issueConnectL2R(left, right) + case (Output, Output) => issueConnectL2R(left, right) + case (Output, Internal) => issueConnectL2R(left, right) - case (Some(Input), Some(Output)) => throw NeitherDriverException - case (Some(Output), Some(Input)) => throw BothDriversException - case (None, _) => throw UnknownRelationException + case (Input, Output) => throw NeitherDriverException + case (Output, Input) => throw BothDriversException + case (Internal, _) => throw UnknownRelationException } } @@ -188,39 +187,17 @@ object BiConnect { else if( (context_mod == left_mod) && (context_mod == right_mod) ) { ((left_direction, right_direction): @unchecked) match { // CURRENT MOD CURRENT MOD - case (Some(Input), Some(Output)) => issueConnectL2R(left, right) - case (Some(Input), None) => issueConnectL2R(left, right) - case (None, Some(Output)) => issueConnectL2R(left, right) + case (Input, Output) => issueConnectL2R(left, right) + case (Input, Internal) => issueConnectL2R(left, right) + case (Internal, Output) => issueConnectL2R(left, right) - case (Some(Output), Some(Input)) => issueConnectR2L(left, right) - case (Some(Output), None) => issueConnectR2L(left, right) - case (None, Some(Input)) => issueConnectR2L(left, right) + case (Output, Input) => issueConnectR2L(left, right) + case (Output, Internal) => issueConnectR2L(left, right) + case (Internal, Input) => issueConnectR2L(left, right) - case (Some(Input), Some(Input)) => { - if (connectCompileOptions.dontAssumeDirectionality) { - throw BothDriversException - } else { - (left.binding, right.binding) match { - case (PortBinding(_, _), PortBinding(_, _)) => throw BothDriversException - case (PortBinding(_, _), _) => issueConnectL2R(left, right) - case (_, PortBinding(_, _)) => issueConnectR2L(left, right) - case _ => throw BothDriversException - } - } - } - case (Some(Output), Some(Output)) => { - if (connectCompileOptions.dontAssumeDirectionality) { - throw BothDriversException - } else { - (left.binding, right.binding) match { - case (PortBinding(_, _), PortBinding(_, _)) => throw BothDriversException - case (PortBinding(_, _), _) => issueConnectR2L(left, right) - case (_, PortBinding(_, _)) => issueConnectL2R(left, right) - case _ => throw BothDriversException - } - } - } - case (None, None) => { + case (Input, Input) => throw BothDriversException + case (Output, Output) => throw BothDriversException + case (Internal, Internal) => { if (connectCompileOptions.dontAssumeDirectionality) { throw UnknownDriverException } else { @@ -239,18 +216,18 @@ object BiConnect { // Thus both nodes must be ports and have a direction hint ((left_direction, right_direction): @unchecked) match { // CHILD MOD CHILD MOD - case (Some(Input), Some(Output)) => issueConnectR2L(left, right) - case (Some(Output), Some(Input)) => issueConnectL2R(left, right) + case (Input, Output) => issueConnectR2L(left, right) + case (Output, Input) => issueConnectL2R(left, right) - case (Some(Input), Some(Input)) => throw NeitherDriverException - case (Some(Output), Some(Output)) => throw BothDriversException - case (_, None) => + case (Input, Input) => throw NeitherDriverException + case (Output, Output) => throw BothDriversException + case (_, Internal) => if (connectCompileOptions.dontAssumeDirectionality) { throw UnknownRelationException } else { issueConnectR2L(left, right) } - case (None, _) => + case (Internal, _) => if (connectCompileOptions.dontAssumeDirectionality) { throw UnknownRelationException } else { diff --git a/chiselFrontend/src/main/scala/chisel3/core/Binder.scala b/chiselFrontend/src/main/scala/chisel3/core/Binder.scala deleted file mode 100644 index d872d7c6..00000000 --- a/chiselFrontend/src/main/scala/chisel3/core/Binder.scala +++ /dev/null @@ -1,64 +0,0 @@ -package chisel3.core - -/** -* A Binder is a function from UnboundBinding to some Binding. -* -* These are used exclusively by Binding.bind and sealed in order to keep -* all of them in one place. There are two flavors of Binders: -* Non-terminal (returns another UnboundBinding): These are used to reformat an -* UnboundBinding (like setting direction) before it is terminally bound. -* Terminal (returns any other Binding): Due to the nature of Bindings, once a -* Data is bound to anything but an UnboundBinding, it is forever locked to -* being that type (as it now represents something in the hardware graph). -* -* Note that some Binders require extra arguments to be constructed, like the -* enclosing Module. -*/ - -sealed trait Binder[Out <: Binding] extends Function1[UnboundBinding, Out]{ - def apply(in: UnboundBinding): Out -} - -// THE NON-TERMINAL BINDERS -// These 'rebind' to another unbound node of different direction! -case object InputBinder extends Binder[UnboundBinding] { - def apply(in: UnboundBinding) = UnboundBinding(Some(Direction.Input)) -} -case object OutputBinder extends Binder[UnboundBinding] { - def apply(in: UnboundBinding) = UnboundBinding(Some(Direction.Output)) -} -case object FlippedBinder extends Binder[UnboundBinding] { - def apply(in: UnboundBinding) = UnboundBinding(in.direction.map(_.flip)) - // TODO(twigg): flipping a None should probably be a warning/error -} -// The need for this should be transient. -case object NoDirectionBinder extends Binder[UnboundBinding] { - def apply(in: UnboundBinding) = UnboundBinding(None) -} - -// THE TERMINAL BINDERS -case object LitBinder extends Binder[LitBinding] { - def apply(in: UnboundBinding) = LitBinding() -} - -case class MemoryPortBinder(enclosure: UserModule) extends Binder[MemoryPortBinding] { - def apply(in: UnboundBinding) = MemoryPortBinding(enclosure) -} - -case class OpBinder(enclosure: UserModule) extends Binder[OpBinding] { - def apply(in: UnboundBinding) = OpBinding(enclosure) -} - -// Notice how PortBinder uses the direction of the UnboundNode -case class PortBinder(enclosure: BaseModule) extends Binder[PortBinding] { - def apply(in: UnboundBinding) = PortBinding(enclosure, in.direction) -} - -case class RegBinder(enclosure: UserModule) extends Binder[RegBinding] { - def apply(in: UnboundBinding) = RegBinding(enclosure) -} - -case class WireBinder(enclosure: UserModule) extends Binder[WireBinding] { - def apply(in: UnboundBinding) = WireBinding(enclosure) -} - diff --git a/chiselFrontend/src/main/scala/chisel3/core/Binding.scala b/chiselFrontend/src/main/scala/chisel3/core/Binding.scala index 87e706b7..2b6f10f6 100644 --- a/chiselFrontend/src/main/scala/chisel3/core/Binding.scala +++ b/chiselFrontend/src/main/scala/chisel3/core/Binding.scala @@ -2,150 +2,69 @@ package chisel3.core import chisel3.internal.Builder.{forcedModule} -/** - * The purpose of a Binding is to indicate what type of hardware 'entity' a - * specific Data's leaf Elements is actually bound to. All Data starts as being - * Unbound (and the whole point of cloneType is to return an unbound version). - * Then, specific API calls take a Data, and return a bound version (either by - * binding the original model or cloneType then binding the clone). For example, - * Reg[T<:Data](...) returns a T bound to RegBinding. - * - * It is considered invariant that all Elements of a single Data are bound to - * the same concrete type of Binding. - * - * These bindings can be checked (e.g. checkSynthesizable) to make sure certain - * operations are valid. For example, arithemetic operations or connections can - * only be executed between synthesizable nodes. These checks are to avoid - * undefined reference errors. - * - * Bindings can carry information about the particular element in the graph it - * represents like: - * - For ports (and unbound), the 'direction' - * - For (relevant) synthesizable nodes, the enclosing Module - * - * TODO(twigg): Enrich the bindings to carry more information like the hosting - * module (when applicable), direction (when applicable), literal info (when - * applicable). Can ensure applicable data only stored on relevant nodes. e.g. - * literal info on LitBinding, direction info on UnboundBinding and PortBinding, - * etc. - * - * TODO(twigg): Currently, bindings only apply at the Element level and an - * Aggregate is considered bound via its elements. May be appropriate to allow - * Aggregates to be bound along with the Elements. However, certain literal and - * port direction information doesn't quite make sense in aggregates. This would - * elegantly handle the empty Vec or Record problem though. - * - * TODO(twigg): Binding is currently done via allElements. It may be more - * elegant if this was instead done as a more explicit tree walk as that allows - * for better errors. - */ - object Binding { - // Two bindings are 'compatible' if they are the same type. - // Check currently kind of weird: just ensures same class - private def compatible(a: Binding, b: Binding): Boolean = a.getClass == b.getClass - private def compatible(nodes: Seq[Binding]): Boolean = - if(nodes.size > 1) - (for((a,b) <- nodes zip nodes.tail) yield compatible(a,b)) - .fold(true)(_&&_) - else true - - case class BindingException(message: String) extends Exception(message) - def AlreadyBoundException(binding: String) = BindingException(s": Already bound to $binding") - def NotSynthesizableException = BindingException(s": Not bound to synthesizable node, currently only Type description") - def MissingIOWrapperException = BindingException(": Missing IO() wrapper") + class BindingException(message: String) extends Exception(message) + /** A function expected a Chisel type but got a hardware object + */ + case class ExpectedChiselTypeException(message: String) extends BindingException(message) + /**A function expected a hardware object but got a Chisel type + */ + case class ExpectedHardwareException(message: String) extends BindingException(message) + /** An aggregate had a mix of specified and unspecified directionality children + */ + case class MixedDirectionAggregateException(message: String) extends BindingException(message) + /** Attempted to re-bind an already bound (directionality or hardware) object + */ + case class RebindingException(message: String) extends BindingException(message) +} - // This recursively walks down the Data tree to look at all the leaf 'Element's - // Will build up an error string in case something goes wrong - // TODO(twigg): Make member function of Data. - // Allows oddities like sample_element to be better hidden - private def walkToBinding(target: Data, checker: Element=>Unit): Unit = target match { - case (element: Element) => checker(element) - case (vec: Vec[Data @unchecked]) => { - try walkToBinding(vec.sample_element, checker) - catch { - case BindingException(message) => throw BindingException(s"(*)$message") - } - for(idx <- 0 until vec.length) { - try walkToBinding(vec(idx), checker) - catch { - case BindingException(message) => throw BindingException(s"($idx)$message") - } - } - } - case (record: Record) => { - for((field, subelem) <- record.elements) { - try walkToBinding(subelem, checker) - catch { - case BindingException(message) => throw BindingException(s".$field$message") - } - } +/** Requires that a node is hardware ("bound") + */ +object requireIsHardware { + def apply(node: Data, msg: String = "") = { + node._parent match { // Compatibility layer hack + case Some(x: BaseModule) => x._autoWrapPorts + case _ => } - } - - // Use walkToBinding to actually rebind the node type - def bind[T<:Data](target: T, binder: Binder[_<:Binding], error_prelude: String): target.type = { - try walkToBinding( - target, - element => element.binding match { - case unbound @ UnboundBinding(_) => { - element.binding = binder(unbound) - } - case binding => throw AlreadyBoundException(binding.toString) - } - ) - catch { - case BindingException(message) => throw BindingException(s"$error_prelude$message") + if (!node.hasBinding) { + val prefix = if (msg.nonEmpty) s"$msg " else "" + throw Binding.ExpectedHardwareException(s"$prefix'$node' must be hardware, not a bare Chisel type") } - target } +} - // Excepts if any root element is already bound - def checkUnbound(target: Data, error_prelude: String): Unit = { - try walkToBinding( - target, - element => element.binding match { - case unbound @ UnboundBinding(_) => {} - case binding => throw AlreadyBoundException(binding.toString) - } - ) - catch { - case BindingException(message) => throw BindingException(s"$error_prelude$message") - } +/** Requires that a node is a chisel type (not hardware, "unbound") + */ +object requireIsChiselType { + def apply(node: Data, msg: String = "") = if (node.hasBinding) { + val prefix = if (msg.nonEmpty) s"$msg " else "" + throw Binding.ExpectedChiselTypeException(s"$prefix'$node' must be a Chisel type, not hardware") } +} - // Excepts if any root element is unbound and thus not on the hardware graph - def checkSynthesizable(target: Data, error_prelude: String): Unit = { - try walkToBinding( - target, - element => { - // Compatibility mode to automatically wrap ports in IO - // TODO: remove me, perhaps by removing Bindings checks from compatibility mode - element._parent match { - case Some(x: BaseModule) => x._autoWrapPorts - case _ => - } - // Actual binding check - element.binding match { - case SynthesizableBinding() => // OK - case binding => { - // Attempt to diagnose common bindings issues, like forgot to wrap IO(...) - element._parent match { - case Some(x: LegacyModule) => - // null check in case we try to access io before it is defined - if ((x.io != null) && (x.io.flatten contains element)) { - throw MissingIOWrapperException - } - case _ => - } - // Fallback generic exception - throw NotSynthesizableException - } - } +// Element only direction used for the Binding system only. +sealed abstract class BindingDirection +object BindingDirection { + /** Internal type or wire + */ + case object Internal extends BindingDirection + /** Module port with output direction + */ + case object Output extends BindingDirection + /** Module port with input direction + */ + case object Input extends BindingDirection + + /** Determine the BindingDirection of an Element given its top binding and resolved direction. + */ + def from(binding: TopBinding, direction: ActualDirection) = { + binding match { + case PortBinding(_) => direction match { + case ActualDirection.Output => Output + case ActualDirection.Input => Input + case dir => throw new RuntimeException(s"Unexpected port element direction '$dir'") } - ) - catch { - case BindingException(message) => throw BindingException(s"$error_prelude$message") + case _ => Internal } } } @@ -153,52 +72,32 @@ object Binding { // Location refers to 'where' in the Module hierarchy this lives sealed trait Binding { def location: Option[BaseModule] - def direction: Option[Direction] } +// Top-level binding representing hardware, not a pointer to another binding (like ChildBinding) +sealed trait TopBinding extends Binding // Constrained-ness refers to whether 'bound by Module boundaries' // An unconstrained binding, like a literal, can be read by everyone -sealed trait UnconstrainedBinding extends Binding { +sealed trait UnconstrainedBinding extends TopBinding { def location = None } // A constrained binding can only be read/written by specific modules // Location will track where this Module is -sealed trait ConstrainedBinding extends Binding { +sealed trait ConstrainedBinding extends TopBinding { def enclosure: BaseModule def location = Some(enclosure) } -// An undirectioned binding means the element represents an internal node -// with no meaningful concept of a direction -sealed trait UndirectionedBinding extends Binding { def direction = None } - -// This is the default binding, represents data not yet positioned in the graph -case class UnboundBinding(direction: Option[Direction]) - extends Binding with UnconstrainedBinding - - -// A synthesizable binding is 'bound into' the hardware graph -object SynthesizableBinding { - def unapply(target: Binding): Boolean = target.isInstanceOf[SynthesizableBinding] - // Type check OK because Binding and SynthesizableBinding is sealed -} -sealed trait SynthesizableBinding extends Binding -case class LitBinding() // will eventually have literal info - extends SynthesizableBinding with UnconstrainedBinding with UndirectionedBinding - -case class MemoryPortBinding(enclosure: UserModule) - extends SynthesizableBinding with ConstrainedBinding with UndirectionedBinding - +// TODO literal info here +case class LitBinding() extends UnconstrainedBinding // TODO(twigg): Ops between unenclosed nodes can also be unenclosed // However, Chisel currently binds all op results to a module -case class OpBinding(enclosure: UserModule) - extends SynthesizableBinding with ConstrainedBinding with UndirectionedBinding - -case class PortBinding(enclosure: BaseModule, direction: Option[Direction]) - extends SynthesizableBinding with ConstrainedBinding - -case class RegBinding(enclosure: UserModule) - extends SynthesizableBinding with ConstrainedBinding with UndirectionedBinding - -case class WireBinding(enclosure: UserModule) - extends SynthesizableBinding with ConstrainedBinding with UndirectionedBinding +case class OpBinding(enclosure: UserModule) extends ConstrainedBinding +case class MemoryPortBinding(enclosure: UserModule) extends ConstrainedBinding +case class PortBinding(enclosure: BaseModule) extends ConstrainedBinding +case class RegBinding(enclosure: UserModule) extends ConstrainedBinding +case class WireBinding(enclosure: UserModule) extends ConstrainedBinding + +case class ChildBinding(parent: Data) extends Binding { + def location = parent.binding.location +} diff --git a/chiselFrontend/src/main/scala/chisel3/core/Bits.scala b/chiselFrontend/src/main/scala/chisel3/core/Bits.scala index 7de7be09..7f660188 100644 --- a/chiselFrontend/src/main/scala/chisel3/core/Bits.scala +++ b/chiselFrontend/src/main/scala/chisel3/core/Bits.scala @@ -17,26 +17,16 @@ import chisel3.internal.firrtl.PrimOp._ /** Element is a leaf data type: it cannot contain other Data objects. Example * uses are for representing primitive data types, like integers and bits. */ -abstract class Element(private[core] val width: Width) extends Data { - /** - * Elements can actually be bound to the hardware graph and thus must store - * that binding information. - */ - private[this] var _binding: Binding = UnboundBinding(None) - // Define setter/getter pairing - // Can only bind something that has not yet been bound. - private[core] def binding_=(target: Binding): Unit = _binding match { - case UnboundBinding(_) => { - _binding = target - _binding +abstract class Element(private[chisel3] val width: Width) extends Data { + private[chisel3] override def bind(target: Binding, parentDirection: UserDirection) { + binding = target + val resolvedDirection = UserDirection.fromParent(parentDirection, userDirection) + direction = resolvedDirection match { + case UserDirection.Unspecified | UserDirection.Flip => ActualDirection.Unspecified + case UserDirection.Output => ActualDirection.Output + case UserDirection.Input => ActualDirection.Input } - case _ => throw Binding.AlreadyBoundException(_binding.toString) - // Other checks should have caught this. } - private[core] def binding = _binding - - /** Return the binding for some bits. */ - def dir: Direction = binding.direction.getOrElse(Direction.Unspecified) private[chisel3] final def allElements: Seq[Element] = Seq(this) def widthKnown: Boolean = width.known @@ -95,7 +85,8 @@ sealed abstract class Bits(width: Width, override val litArg: Option[LitArg]) if (isLit()) { (((litValue() >> x.toInt) & 1) == 1).asBool } else { - Binding.checkSynthesizable(this, s"'this' ($this)") + + requireIsHardware(this, "bits to be indexed") pushOp(DefPrim(sourceInfo, Bool(), BitsExtractOp, this.ref, ILit(x), ILit(x))) } } @@ -138,7 +129,7 @@ sealed abstract class Bits(width: Width, override val litArg: Option[LitArg]) if (isLit()) { ((litValue >> y) & ((BigInt(1) << w) - 1)).asUInt(w.W) } else { - Binding.checkSynthesizable(this, s"'this' ($this)") + requireIsHardware(this, "bits to be sliced") pushOp(DefPrim(sourceInfo, UInt(Width(w)), BitsExtractOp, this.ref, ILit(x), ILit(y))) } } @@ -150,25 +141,25 @@ sealed abstract class Bits(width: Width, override val litArg: Option[LitArg]) apply(x.toInt, y.toInt) private[core] def unop[T <: Data](sourceInfo: SourceInfo, dest: T, op: PrimOp): T = { - Binding.checkSynthesizable(this, s"'this' ($this)") + requireIsHardware(this, "bits operated on") pushOp(DefPrim(sourceInfo, dest, op, this.ref)) } private[core] def binop[T <: Data](sourceInfo: SourceInfo, dest: T, op: PrimOp, other: BigInt): T = { - Binding.checkSynthesizable(this, s"'this' ($this)") + requireIsHardware(this, "bits operated on") pushOp(DefPrim(sourceInfo, dest, op, this.ref, ILit(other))) } private[core] def binop[T <: Data](sourceInfo: SourceInfo, dest: T, op: PrimOp, other: Bits): T = { - Binding.checkSynthesizable(this, s"'this' ($this)") - Binding.checkSynthesizable(other, s"'other' ($other)") + requireIsHardware(this, "bits operated on") + requireIsHardware(other, "bits operated on") pushOp(DefPrim(sourceInfo, dest, op, this.ref, other.ref)) } private[core] def compop(sourceInfo: SourceInfo, op: PrimOp, other: Bits): Bool = { - Binding.checkSynthesizable(this, s"'this' ($this)") - Binding.checkSynthesizable(other, s"'other' ($other)") + requireIsHardware(this, "bits operated on") + requireIsHardware(other, "bits operated on") pushOp(DefPrim(sourceInfo, Bool(), op, this.ref, other.ref)) } private[core] def redop(sourceInfo: SourceInfo, op: PrimOp): Bool = { - Binding.checkSynthesizable(this, s"'this' ($this)") + requireIsHardware(this, "bits operated on") pushOp(DefPrim(sourceInfo, Bool(), op, this.ref)) } @@ -404,7 +395,6 @@ sealed class UInt private[core] (width: Width, lit: Option[ULit] = None) private[core] override def cloneTypeWidth(w: Width): this.type = new UInt(w).asInstanceOf[this.type] - private[chisel3] def toType = s"UInt$width" // TODO: refactor to share documentation with Num or add independent scaladoc final def unary_- (): UInt = macro SourceInfoTransform.noArg @@ -547,7 +537,7 @@ trait UIntFactory { val lit = ULit(value, width) val result = new UInt(lit.width, Some(lit)) // Bind result to being an Literal - result.binding = LitBinding() + result.bind(LitBinding()) result } @@ -572,7 +562,6 @@ sealed class SInt private[core] (width: Width, lit: Option[SLit] = None) private[core] override def cloneTypeWidth(w: Width): this.type = new SInt(w).asInstanceOf[this.type] - private[chisel3] def toType = s"SInt$width" final def unary_- (): SInt = macro SourceInfoTransform.noArg final def unary_-% (): SInt = macro SourceInfoTransform.noArg @@ -703,7 +692,7 @@ trait SIntFactory { val lit = SLit(value, width) val result = new SInt(lit.width, Some(lit)) // Bind result to being an Literal - result.binding = LitBinding() + result.bind(LitBinding()) result } } @@ -765,7 +754,7 @@ trait BoolFactory { protected[chisel3] def Lit(x: Boolean): Bool = { val result = new Bool(Some(ULit(if (x) 1 else 0, Width(1)))) // Bind result to being an Literal - result.binding = LitBinding() + result.bind(LitBinding()) result } } @@ -794,7 +783,6 @@ sealed class FixedPoint private (width: Width, val binaryPoint: BinaryPoint, lit private[core] override def cloneTypeWidth(w: Width): this.type = new FixedPoint(w, binaryPoint).asInstanceOf[this.type] - private[chisel3] def toType = s"Fixed$width$binaryPoint" override def connect (that: Data)(implicit sourceInfo: SourceInfo, connectCompileOptions: CompileOptions): Unit = that match { case _: FixedPoint => super.connect(that) @@ -946,9 +934,6 @@ object FixedPoint { /** Create an FixedPoint type or port with fixed width. */ def apply(width: Width, binaryPoint: BinaryPoint): FixedPoint = new FixedPoint(width, binaryPoint) - /** Create an FixedPoint port with inferred width. */ - def apply(dir: Direction): FixedPoint = apply(dir, Width(), BinaryPoint()) - /** Create an FixedPoint literal with inferred width from BigInt. * Use PrivateObject to force users to specify width and binaryPoint by name */ @@ -991,11 +976,10 @@ object FixedPoint { } /** Create an FixedPoint port with specified width and binary position. */ - def apply(dir: Direction, width: Width, binaryPoint: BinaryPoint): FixedPoint = new FixedPoint(width, binaryPoint) def apply(value: BigInt, width: Width, binaryPoint: BinaryPoint): FixedPoint = { val lit = FPLit(value, width, binaryPoint) val newLiteral = new FixedPoint(lit.width, lit.binaryPoint, Some(lit)) - newLiteral.binding = LitBinding() + newLiteral.bind(LitBinding()) newLiteral } @@ -1043,8 +1027,6 @@ object FixedPoint { final class Analog private (width: Width) extends Element(width) { require(width.known, "Since Analog is only for use in BlackBoxes, width must be known") - private[chisel3] def toType = s"Analog$width" - private[core] override def typeEquivalent(that: Data): Boolean = that.isInstanceOf[Analog] && this.width == that.width @@ -1057,9 +1039,23 @@ final class Analog private (width: Width) extends Element(width) { // Define setter/getter pairing // Analog can only be bound to Ports and Wires (and Unbound) - private[core] override def binding_=(target: Binding): Unit = target match { - case (_: UnboundBinding | _: WireBinding | PortBinding(_, None)) => super.binding_=(target) - case _ => throwException("Only Wires and Ports can be of type Analog") + private[chisel3] override def bind(target: Binding, parentDirection: UserDirection) { + UserDirection.fromParent(parentDirection, userDirection) match { + case UserDirection.Unspecified | UserDirection.Flip => + case x => throwException(s"Analog may not have explicit direction, got '$x'") + } + val targetTopBinding = target match { + case target: TopBinding => target + case ChildBinding(parent) => parent.topBinding + } + + // Analog counts as different directions based on binding context + targetTopBinding match { + case WireBinding(_) => direction = ActualDirection.Unspecified // internal wire + case PortBinding(_) => direction = ActualDirection.Bidirectional(ActualDirection.Default) + case x => throwException(s"Analog can only be Ports and Wires, not '$x'") + } + binding = target } override def do_asUInt(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = diff --git a/chiselFrontend/src/main/scala/chisel3/core/BlackBox.scala b/chiselFrontend/src/main/scala/chisel3/core/BlackBox.scala index cd072ba9..6d8e85a4 100644 --- a/chiselFrontend/src/main/scala/chisel3/core/BlackBox.scala +++ b/chiselFrontend/src/main/scala/chisel3/core/BlackBox.scala @@ -74,13 +74,7 @@ abstract class ExtModule(val params: Map[String, Param] = Map.empty[String, Para id._onModuleClose } - val firrtlPorts = for (port <- getModulePorts) yield { - // Port definitions need to know input or output at top-level. - // By FIRRTL semantics, 'flipped' becomes an Input - val direction = if(Data.isFirrtlFlipped(port)) Direction.Input else Direction.Output - Port(port, direction) - } - + val firrtlPorts = getModulePorts map {port => Port(port, port.userDirection)} val component = DefBlackBox(this, name, firrtlPorts, params) _component = Some(component) component @@ -132,7 +126,7 @@ abstract class ExtModule(val params: Map[String, Param] = Map.empty[String, Para */ abstract class BlackBox(val params: Map[String, Param] = Map.empty[String, Param])(implicit compileOptions: CompileOptions) extends BaseBlackBox { def io: Record - + // Allow access to bindings from the compatibility package protected def _ioPortBound() = portsContains(io) @@ -165,19 +159,13 @@ abstract class BlackBox(val params: Map[String, Param] = Map.empty[String, Param id._onModuleClose } - val firrtlPorts = for ((_, port) <- namedPorts) yield { - // Port definitions need to know input or output at top-level. - // By FIRRTL semantics, 'flipped' becomes an Input - val direction = if(Data.isFirrtlFlipped(port)) Direction.Input else Direction.Output - Port(port, direction) - } - + val firrtlPorts = namedPorts map {namedPort => Port(namedPort._2, namedPort._2.userDirection)} val component = DefBlackBox(this, name, firrtlPorts, params) _component = Some(component) component } - private[core] def initializeInParent() { + private[core] def initializeInParent() { for ((_, port) <- io.elements) { pushCommand(DefInvalid(UnlocatableSourceInfo, port.ref)) } diff --git a/chiselFrontend/src/main/scala/chisel3/core/ChiselAnnotation.scala b/chiselFrontend/src/main/scala/chisel3/core/ChiselAnnotation.scala index ad4050f3..07546406 100644 --- a/chiselFrontend/src/main/scala/chisel3/core/ChiselAnnotation.scala +++ b/chiselFrontend/src/main/scala/chisel3/core/ChiselAnnotation.scala @@ -2,6 +2,8 @@ package chisel3.core +import scala.language.existentials + import chisel3.internal.{Builder, InstanceId} import firrtl.Transform import firrtl.annotations.{Annotation, CircuitName, ComponentName, ModuleName} @@ -56,7 +58,7 @@ object dontTouch { // scalastyle:ignore object.name */ def apply[T <: Data](data: T)(implicit compileOptions: CompileOptions): T = { if (compileOptions.checkSynthesizable) { - Binding.checkSynthesizable(data, s"$data") + requireIsHardware(data, "Data marked dontTouch") } // TODO unify with firrtl.transforms.DontTouchAnnotation val anno = ChiselAnnotation(data, classOf[firrtl.Transform], "DONTtouch!") diff --git a/chiselFrontend/src/main/scala/chisel3/core/Clock.scala b/chiselFrontend/src/main/scala/chisel3/core/Clock.scala new file mode 100644 index 00000000..f682310b --- /dev/null +++ b/chiselFrontend/src/main/scala/chisel3/core/Clock.scala @@ -0,0 +1,34 @@ +// See LICENSE for license details. + +package chisel3.core + +import chisel3.internal.Builder.{pushOp} +import chisel3.internal.firrtl._ +import chisel3.internal.sourceinfo._ +import chisel3.internal.firrtl.PrimOp.AsUIntOp + +object Clock { + def apply(): Clock = new Clock +} + +// TODO: Document this. +sealed class Clock extends Element(Width(1)) { + def cloneType: this.type = Clock().asInstanceOf[this.type] + + private[core] def typeEquivalent(that: Data): Boolean = + this.getClass == that.getClass + + override def connect(that: Data)(implicit sourceInfo: SourceInfo, connectCompileOptions: CompileOptions): Unit = that match { + case _: Clock => super.connect(that)(sourceInfo, connectCompileOptions) + case _ => super.badConnect(that)(sourceInfo) + } + + /** Not really supported */ + def toPrintable: Printable = PString("CLOCK") + + override def do_asUInt(implicit sourceInfo: SourceInfo, connectCompileOptions: CompileOptions): UInt = pushOp(DefPrim(sourceInfo, UInt(this.width), AsUIntOp, ref)) + private[core] override def connectFromBits(that: Bits)(implicit sourceInfo: SourceInfo, + compileOptions: CompileOptions): Unit = { + this := that + } +} diff --git a/chiselFrontend/src/main/scala/chisel3/core/CompileOptions.scala b/chiselFrontend/src/main/scala/chisel3/core/CompileOptions.scala index 803e6c0f..55e6d18c 100644 --- a/chiselFrontend/src/main/scala/chisel3/core/CompileOptions.scala +++ b/chiselFrontend/src/main/scala/chisel3/core/CompileOptions.scala @@ -16,9 +16,6 @@ trait CompileOptions { val dontTryConnectionsSwapped: Boolean // If connection directionality is not explicit, do not use heuristics to attempt to determine it. val dontAssumeDirectionality: Boolean - // Issue a deprecation warning if Data.{flip, asInput,asOutput} is used - // instead of Flipped, Input, or Output. - val deprecateOldDirectionMethods: Boolean // Check that referenced Data have actually been declared. val checkSynthesizable: Boolean } @@ -46,9 +43,6 @@ object ExplicitCompileOptions { val dontTryConnectionsSwapped: Boolean, // If connection directionality is not explicit, do not use heuristics to attempt to determine it. val dontAssumeDirectionality: Boolean, - // Issue a deprecation warning if Data.{flip, asInput,asOutput} is used - // instead of Flipped, Input, or Output. - val deprecateOldDirectionMethods: Boolean, // Check that referenced Data have actually been declared. val checkSynthesizable: Boolean ) extends CompileOptions @@ -61,7 +55,6 @@ object ExplicitCompileOptions { declaredTypeMustBeUnbound = false, dontTryConnectionsSwapped = false, dontAssumeDirectionality = false, - deprecateOldDirectionMethods = false, checkSynthesizable = false ) @@ -72,7 +65,6 @@ object ExplicitCompileOptions { declaredTypeMustBeUnbound = true, dontTryConnectionsSwapped = true, dontAssumeDirectionality = true, - deprecateOldDirectionMethods = true, checkSynthesizable = true ) } diff --git a/chiselFrontend/src/main/scala/chisel3/core/Data.scala b/chiselFrontend/src/main/scala/chisel3/core/Data.scala index 580dabe0..41e09a5b 100644 --- a/chiselFrontend/src/main/scala/chisel3/core/Data.scala +++ b/chiselFrontend/src/main/scala/chisel3/core/Data.scala @@ -8,16 +8,65 @@ import chisel3.internal._ import chisel3.internal.Builder.{pushCommand, pushOp} import chisel3.internal.firrtl._ import chisel3.internal.sourceinfo._ -import chisel3.internal.firrtl.PrimOp.AsUIntOp -sealed abstract class Direction(name: String) { - override def toString: String = name - def flip: Direction +/** User-specified directions. + */ +sealed abstract class UserDirection +object UserDirection { + /** Default user direction, also meaning 'not-flipped' + */ + case object Unspecified extends UserDirection + /** Node and its children are forced as output + */ + case object Output extends UserDirection + /** Node and ites children are forced as inputs + */ + case object Input extends UserDirection + /** Mainly for containers, children are flipped. + */ + case object Flip extends UserDirection + + def flip(dir: UserDirection) = dir match { + case Unspecified => Flip + case Flip => Unspecified + case Output => Input + case Input => Output + } + + /** Returns the effective UserDirection of this node given the parent's effective UserDirection + * and the user-specified UserDirection of this node. + */ + def fromParent(parentDirection: UserDirection, thisDirection: UserDirection) = + (parentDirection, thisDirection) match { + case (UserDirection.Output, _) => UserDirection.Output + case (UserDirection.Input, _) => UserDirection.Input + case (UserDirection.Unspecified, thisDirection) => thisDirection + case (UserDirection.Flip, thisDirection) => UserDirection.flip(thisDirection) + } } -object Direction { - object Input extends Direction("input") { override def flip: Direction = Output } - object Output extends Direction("output") { override def flip: Direction = Input } - object Unspecified extends Direction("unspecified") { override def flip: Direction = Input } + +/** Resolved directions for both leaf and container nodes, only visible after + * a node is bound (since higher-level specifications like Input and Output + * can override directions). + */ +sealed abstract class ActualDirection + +object ActualDirection { + /** Undirectioned, struct-like + */ + case object Unspecified extends ActualDirection + /** Output element, or container with all outputs (even if forced) + */ + case object Output extends ActualDirection + /** Input element, or container with all inputs (even if forced) + */ + case object Input extends ActualDirection + + sealed abstract class BidirectionalDirection + case object Default extends BidirectionalDirection + case object Flipped extends BidirectionalDirection + + case class Bidirectional(dir: BidirectionalDirection) extends ActualDirection } @deprecated("debug doesn't do anything in Chisel3 as no pruning happens in the frontend", "chisel3") @@ -25,8 +74,17 @@ object debug { // scalastyle:ignore object.name def apply (arg: Data): Data = arg } +/** Experimental hardware construction reflection API + */ object DataMirror { def widthOf(target: Data): Width = target.width + def userDirectionOf(target: Data): UserDirection = target.userDirection + def directionOf(target: Data): ActualDirection = { + requireIsHardware(target, "node requested directionality on") + target.direction + } + // TODO: really not a reflection-style API, but a workaround for dir in the compatibility package + def isSynthesizable(target: Data) = target.hasBinding } /** Creates a clone of the super-type of the input elements. Super-type is defined as: @@ -87,88 +145,24 @@ private[core] object cloneSupertype { * Thus, an error will be thrown if these are used on bound Data */ object Input { - def apply[T<:Data](source: T)(implicit compileOptions: CompileOptions): T = { - val target = source.chiselCloneType - Data.setFirrtlDirection(target, Direction.Input) - Binding.bind(target, InputBinder, "Error: Cannot set as input ") + def apply[T<:Data](source: T): T = { + val out = source.cloneType + out.userDirection = UserDirection.Input + out } } object Output { - def apply[T<:Data](source: T)(implicit compileOptions: CompileOptions): T = { - val target = source.chiselCloneType - Data.setFirrtlDirection(target, Direction.Output) - Binding.bind(target, OutputBinder, "Error: Cannot set as output ") + def apply[T<:Data](source: T): T = { + val out = source.cloneType + out.userDirection = UserDirection.Output + out } } object Flipped { - def apply[T<:Data](source: T)(implicit compileOptions: CompileOptions): T = { - val target = source.chiselCloneType - Data.setFirrtlDirection(target, Data.getFirrtlDirection(source).flip) - Binding.bind(target, FlippedBinder, "Error: Cannot flip ") - } -} - -object Data { - /** - * This function returns true if the FIRRTL type of this Data should be flipped - * relative to other nodes. - * - * Note that the current scheme only applies Flip to Elements or Vec chains of - * Elements. - * - * A Record is never marked flip, instead preferring its root fields to be marked - * - * The Vec check is due to the fact that flip must be factored out of the vec, ie: - * must have flip field: Vec(UInt) instead of field: Vec(flip UInt) - */ - private[chisel3] def isFlipped(target: Data): Boolean = target match { - case (element: Element) => element.binding.direction == Some(Direction.Input) - case (vec: Vec[Data @unchecked]) => isFlipped(vec.sample_element) - case (record: Record) => false - } - - /** This function returns the "firrtl" flipped-ness for the specified object. - * - * @param target the object for which we want the "firrtl" flipped-ness. - */ - private[chisel3] def isFirrtlFlipped(target: Data): Boolean = { - Data.getFirrtlDirection(target) == Direction.Input - } - - /** This function gets the "firrtl" direction for the specified object. - * - * @param target the object for which we want to get the "firrtl" direction. - */ - private[chisel3] def getFirrtlDirection(target: Data): Direction = target match { - case (vec: Vec[Data @unchecked]) => vec.sample_element.firrtlDirection - case _ => target.firrtlDirection - } - - /** This function sets the "firrtl" direction for the specified object. - * - * @param target the object for which we want to set the "firrtl" direction. - */ - private[chisel3] def setFirrtlDirection(target: Data, direction: Direction): Unit = target match { - case (vec: Vec[Data @unchecked]) => vec.sample_element.firrtlDirection = direction - case _ => target.firrtlDirection = direction - } - - implicit class AddDirectionToData[T<:Data](val target: T) extends AnyVal { - def asInput(implicit opts: CompileOptions): T = { - if (opts.deprecateOldDirectionMethods) - Builder.deprecated("Input(Data) should be used over Data.asInput") - Input(target) - } - def asOutput(implicit opts: CompileOptions): T = { - if (opts.deprecateOldDirectionMethods) - Builder.deprecated("Output(Data) should be used over Data.asOutput") - Output(target) - } - def flip()(implicit opts: CompileOptions): T = { - if (opts.deprecateOldDirectionMethods) - Builder.deprecated("Flipped(Data) should be used over Data.flip") - Flipped(target) - } + def apply[T<:Data](source: T): T = { + val out = source.cloneType + out.userDirection = UserDirection.flip(source.userDirection) + out } } @@ -188,6 +182,76 @@ abstract class Data extends HasId { } } + // User-specified direction, local at this node only. + // Note that the actual direction of this node can differ from child and parent userDirection. + private var _userDirection: UserDirection = UserDirection.Unspecified + private[chisel3] def userDirection: UserDirection = _userDirection + private[core] def userDirection_=(direction: UserDirection) = { + if (_userDirection != UserDirection.Unspecified) { + this match { + // Anything flies in compatibility mode + case t: Record if !t.compileOptions.dontAssumeDirectionality => + case _ => throw Binding.RebindingException(s"Attempted reassignment of user direction to $this") + } + } + _userDirection = direction + } + + /** This overwrites a relative UserDirection 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! + */ + private[core] def _assignCompatibilityExplicitDirection: Unit = { + _userDirection match { + case UserDirection.Unspecified => _userDirection = UserDirection.Output + case UserDirection.Flip => _userDirection = UserDirection.Input + case UserDirection.Input | UserDirection.Output => // nothing to do + } + } + + // Binding stores information about this node's position in the hardware graph. + // This information is supplemental (more than is necessary to generate FIRRTL) and is used to + // perform checks in Chisel, where more informative error messages are possible. + private var _binding: Option[Binding] = None + private[core] def hasBinding = _binding.isDefined + // Only valid after node is bound (synthesizable), crashes otherwise + private[core] def binding = _binding.get + protected def binding_=(target: Binding) { + if (_binding.isDefined) { + throw Binding.RebindingException(s"Attempted reassignment of binding to $this") + } + _binding = Some(target) + } + + private[core] def topBinding: TopBinding = { + binding match { + case ChildBinding(parent) => parent.topBinding + case topBinding: TopBinding => topBinding + } + } + + /** Binds this node to the hardware graph. + * parentDirection is the direction of the parent node, or Unspecified (default) if the target + * node is the top-level. + * binding and direction are valid after this call completes. + */ + private[chisel3] def bind(target: Binding, parentDirection: UserDirection = UserDirection.Unspecified) + + // Both _direction and _resolvedUserDirection are saved versions of computed variables (for + // efficiency, avoid expensive recomputation of frequent operations). + // Both are only valid after binding is set. + + // Direction of this node, accounting for parents (force Input / Output) and children. + private var _direction: Option[ActualDirection] = None + + private[chisel3] def direction: ActualDirection = _direction.get + private[core] def direction_=(actualDirection: ActualDirection) { + if (_direction.isDefined) { + throw Binding.RebindingException(s"Attempted reassignment of resolved direction to $this") + } + _direction = Some(actualDirection) + } + // Return ALL elements at root of this type. // Contasts with flatten, which returns just Bits // TODO: refactor away this, this is outside the scope of Data @@ -197,8 +261,8 @@ abstract class Data extends HasId { throwException(s"cannot connect ${this} and ${that}") private[chisel3] def connect(that: Data)(implicit sourceInfo: SourceInfo, connectCompileOptions: CompileOptions): Unit = { if (connectCompileOptions.checkSynthesizable) { - Binding.checkSynthesizable(this, s"'this' ($this)") - Binding.checkSynthesizable(that, s"'that' ($that)") + requireIsHardware(this, "data to be connected") + requireIsHardware(that, "data to be connected") try { MonoConnect.connect(sourceInfo, connectCompileOptions, this, that, Builder.forcedUserModule) } catch { @@ -213,8 +277,8 @@ abstract class Data extends HasId { } private[chisel3] def bulkConnect(that: Data)(implicit sourceInfo: SourceInfo, connectCompileOptions: CompileOptions): Unit = { if (connectCompileOptions.checkSynthesizable) { - Binding.checkSynthesizable(this, s"'this' ($this)") - Binding.checkSynthesizable(that, s"'that' ($that)") + requireIsHardware(this, s"data to be bulk-connected") + requireIsHardware(that, s"data to be bulk-connected") try { BiConnect.connect(sourceInfo, connectCompileOptions, this, that, Builder.forcedUserModule) } catch { @@ -235,8 +299,7 @@ abstract class Data extends HasId { private[chisel3] def lref: Node = Node(this) private[chisel3] def ref: Arg = if (isLit) litArg.get else lref - private[chisel3] def toType: String - private[core] def width: Width + private[chisel3] def width: Width private[core] def legacyConnect(that: Data)(implicit sourceInfo: SourceInfo): Unit /** cloneType must be defined for any Chisel object extending Data. @@ -252,21 +315,10 @@ abstract class Data extends HasId { * then performs any fixups required to reconstruct the appropriate core state of the cloned object. * @return a copy of the object with appropriate core state. */ - def chiselCloneType(implicit compileOptions: CompileOptions): this.type = { - // TODO: refactor away allElements, handle this with Aggregate/Element match inside Bindings - - // Call the user-supplied cloneType method - val clone = this.cloneType - // In compatibility mode, simply return cloneType; otherwise, propagate - // direction and flippedness. - if (compileOptions.checkSynthesizable) { - Data.setFirrtlDirection(clone, Data.getFirrtlDirection(this)) - //TODO(twigg): Do recursively for better error messages - for((clone_elem, source_elem) <- clone.allElements zip this.allElements) { - clone_elem.binding = UnboundBinding(source_elem.binding.direction) - Data.setFirrtlDirection(clone_elem, Data.getFirrtlDirection(source_elem)) - } - } + def chiselCloneType: this.type = { + val clone = this.cloneType // get a fresh object, without bindings + // Only the top-level direction needs to be fixed up, cloneType should do the rest + clone.userDirection = userDirection clone } final def := (that: Data)(implicit sourceInfo: SourceInfo, connectionCompileOptions: CompileOptions): Unit = this.connect(that)(sourceInfo, connectionCompileOptions) @@ -322,11 +374,6 @@ abstract class Data extends HasId { def do_asUInt(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt - // firrtlDirection is the direction we report to firrtl. - // It maintains the user-specified value (as opposed to the "actual" or applied/propagated value). - // NOTE: This should only be used for emitting acceptable firrtl. - // The Element.dir should be used for any tests involving direction. - private var firrtlDirection: Direction = Direction.Unspecified /** Default pretty printing */ def toPrintable: Printable } @@ -349,7 +396,7 @@ object Wire { def apply[T <: Data](t: T, init: T)(implicit compileOptions: CompileOptions): T = { implicit val noSourceInfo = UnlocatableSourceInfo val x = apply(t) - Binding.checkSynthesizable(init, s"'init' ($init)") + requireIsHardware(init, "wire initializer") x := init x } @@ -358,7 +405,7 @@ object Wire { val x = t.chiselCloneType // Bind each element of x to being a Wire - Binding.bind(x, WireBinder(Builder.forcedUserModule), "Error: t") + x.bind(WireBinding(Builder.forcedUserModule)) pushCommand(DefWire(sourceInfo, x)) pushCommand(DefInvalid(sourceInfo, x.ref)) @@ -366,38 +413,3 @@ object Wire { x } } - -object Clock { - def apply(): Clock = new Clock - def apply(dir: Direction)(implicit compileOptions: CompileOptions): Clock = { - val result = apply() - dir match { - case Direction.Input => Input(result) - case Direction.Output => Output(result) - case Direction.Unspecified => result - } - } -} - -// TODO: Document this. -sealed class Clock extends Element(Width(1)) { - def cloneType: this.type = Clock().asInstanceOf[this.type] - private[chisel3] def toType = "Clock" - - private[core] def typeEquivalent(that: Data): Boolean = - this.getClass == that.getClass - - override def connect (that: Data)(implicit sourceInfo: SourceInfo, connectCompileOptions: CompileOptions): Unit = that match { - case _: Clock => super.connect(that)(sourceInfo, connectCompileOptions) - case _ => super.badConnect(that)(sourceInfo) - } - - /** Not really supported */ - def toPrintable: Printable = PString("CLOCK") - - override def do_asUInt(implicit sourceInfo: SourceInfo, connectCompileOptions: CompileOptions): UInt = pushOp(DefPrim(sourceInfo, UInt(this.width), AsUIntOp, ref)) - private[core] override def connectFromBits(that: Bits)(implicit sourceInfo: SourceInfo, - compileOptions: CompileOptions): Unit = { - this := that - } -} diff --git a/chiselFrontend/src/main/scala/chisel3/core/Mem.scala b/chiselFrontend/src/main/scala/chisel3/core/Mem.scala index 03c484b0..47d48061 100644 --- a/chiselFrontend/src/main/scala/chisel3/core/Mem.scala +++ b/chiselFrontend/src/main/scala/chisel3/core/Mem.scala @@ -21,8 +21,6 @@ object Mem { def apply[T <: Data](size: Int, t: T): Mem[T] = macro MemTransform.apply[T] def do_apply[T <: Data](size: Int, t: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Mem[T] = { val mt = t.chiselCloneType - Binding.bind(mt, NoDirectionBinder, "Error: fresh t") - // TODO(twigg): Remove need for this Binding val mem = new Mem(mt, size) pushCommand(DefMemory(sourceInfo, mem, mt, size)) @@ -87,7 +85,7 @@ sealed abstract class MemBase[T <: Data](t: T, val length: Int) extends HasId { } private def makePort(sourceInfo: SourceInfo, idx: UInt, dir: MemPortDirection)(implicit compileOptions: CompileOptions): T = { - Binding.checkSynthesizable(idx, s"'idx' ($idx)") + requireIsHardware(idx, "memory port index") val i = Vec.truncateIndex(idx, length)(sourceInfo, compileOptions) val port = pushCommand( @@ -95,7 +93,7 @@ sealed abstract class MemBase[T <: Data](t: T, val length: Int) extends HasId { t.chiselCloneType, Node(this), dir, i.ref, Node(Builder.forcedClock)) ).id // Bind each element of port to being a MemoryPort - Binding.bind(port, MemoryPortBinder(Builder.forcedUserModule), "Error: Fresh t") + port.bind(MemoryPortBinding(Builder.forcedUserModule)) port } } @@ -124,9 +122,6 @@ object SyncReadMem { def do_apply[T <: Data](size: Int, t: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SyncReadMem[T] = { val mt = t.chiselCloneType - Binding.bind(mt, NoDirectionBinder, "Error: fresh t") - // TODO(twigg): Remove need for this Binding - val mem = new SyncReadMem(mt, size) pushCommand(DefSeqMemory(sourceInfo, mem, mt, size)) mem diff --git a/chiselFrontend/src/main/scala/chisel3/core/Module.scala b/chiselFrontend/src/main/scala/chisel3/core/Module.scala index b512ed75..0f081daf 100644 --- a/chiselFrontend/src/main/scala/chisel3/core/Module.scala +++ b/chiselFrontend/src/main/scala/chisel3/core/Module.scala @@ -219,8 +219,34 @@ abstract class BaseModule extends HasId { */ protected def IO[T<:Data](iodef: T): iodef.type = { require(!_closed, "Can't add more ports after module close") + requireIsChiselType(iodef, "io type") + + // Compatibility code: Chisel2 did not require explicit direction on nodes + // (unspecified treated as output, and flip on nothing was input). + // This sets assigns the explicit directions required by newer semantics on + // Bundles defined in compatibility mode. + // This recursively walks the tree, and assigns directions if no explicit + // direction given by upper-levels (override Input / Output) AND element is + // directly inside a compatibility Bundle determined by compile options. + def assignCompatDir(data: Data, insideCompat: Boolean): Unit = { + data match { + case data: Element if insideCompat => data._assignCompatibilityExplicitDirection + case data: Element => // Not inside a compatibility Bundle, nothing to be done + case data: Aggregate => data.userDirection match { + // Recurse into children to ensure explicit direction set somewhere + case UserDirection.Unspecified | UserDirection.Flip => data match { + case data: Record if (!data.compileOptions.dontAssumeDirectionality) => + data.getElements.foreach(assignCompatDir(_, true)) + case _ => data.getElements.foreach(assignCompatDir(_, false)) + } + case UserDirection.Input | UserDirection.Output => // forced assign, nothing to do + } + } + } + assignCompatDir(iodef, false) + // Bind each element of the iodef to being a Port - Binding.bind(iodef, PortBinder(this), "Error: iodef") + iodef.bind(PortBinding(this)) _ports += iodef iodef } diff --git a/chiselFrontend/src/main/scala/chisel3/core/MonoConnect.scala b/chiselFrontend/src/main/scala/chisel3/core/MonoConnect.scala index 80e96ce7..3c34785f 100644 --- a/chiselFrontend/src/main/scala/chisel3/core/MonoConnect.scala +++ b/chiselFrontend/src/main/scala/chisel3/core/MonoConnect.scala @@ -120,24 +120,23 @@ object MonoConnect { // This function checks if element-level connection operation allowed. // Then it either issues it or throws the appropriate exception. def elemConnect(implicit sourceInfo: SourceInfo, connectCompileOptions: CompileOptions, sink: Element, source: Element, context_mod: UserModule): Unit = { - import Direction.{Input, Output} // Using extensively so import these + import BindingDirection.{Internal, Input, Output} // Using extensively so import these // If source has no location, assume in context module // This can occur if is a literal, unbound will error previously val sink_mod: BaseModule = sink.binding.location.getOrElse(throw UnwritableSinkException) val source_mod: BaseModule = source.binding.location.getOrElse(context_mod) - val sink_direction: Option[Direction] = sink.binding.direction - val source_direction: Option[Direction] = source.binding.direction - // None means internal + val sink_direction = BindingDirection.from(sink.topBinding, sink.direction) + val source_direction = BindingDirection.from(source.topBinding, source.direction) // CASE: Context is same module that both left node and right node are in if( (context_mod == sink_mod) && (context_mod == source_mod) ) { ((sink_direction, source_direction): @unchecked) match { // SINK SOURCE // CURRENT MOD CURRENT MOD - case (Some(Output), _) => issueConnect(sink, source) - case (None, _) => issueConnect(sink, source) - case (Some(Input), _) => throw UnwritableSinkException + case (Output, _) => issueConnect(sink, source) + case (Internal, _) => issueConnect(sink, source) + case (Input, _) => throw UnwritableSinkException } } @@ -148,19 +147,19 @@ object MonoConnect { ((sink_direction, source_direction): @unchecked) match { // SINK SOURCE // CURRENT MOD CHILD MOD - case (None, Some(Output)) => issueConnect(sink, source) - case (None, Some(Input)) => issueConnect(sink, source) - case (Some(Output), Some(Output)) => issueConnect(sink, source) - case (Some(Output), Some(Input)) => issueConnect(sink, source) - case (_, None) => { + case (Internal, Output) => issueConnect(sink, source) + case (Internal, Input) => issueConnect(sink, source) + case (Output, Output) => issueConnect(sink, source) + case (Output, Input) => issueConnect(sink, source) + case (_, Internal) => { if (!(connectCompileOptions.dontAssumeDirectionality)) { issueConnect(sink, source) } else { throw UnreadableSourceException } } - case (Some(Input), Some(Output)) if (!(connectCompileOptions.dontTryConnectionsSwapped)) => issueConnect(source, sink) - case (Some(Input), _) => throw UnwritableSinkException + case (Input, Output) if (!(connectCompileOptions.dontTryConnectionsSwapped)) => issueConnect(source, sink) + case (Input, _) => throw UnwritableSinkException } } @@ -171,9 +170,9 @@ object MonoConnect { ((sink_direction, source_direction): @unchecked) match { // SINK SOURCE // CHILD MOD CURRENT MOD - case (Some(Input), _) => issueConnect(sink, source) - case (Some(Output), _) => throw UnwritableSinkException - case (None, _) => throw UnwritableSinkException + case (Input, _) => issueConnect(sink, source) + case (Output, _) => throw UnwritableSinkException + case (Internal, _) => throw UnwritableSinkException } } @@ -187,17 +186,17 @@ object MonoConnect { ((sink_direction, source_direction): @unchecked) match { // SINK SOURCE // CHILD MOD CHILD MOD - case (Some(Input), Some(Input)) => issueConnect(sink, source) - case (Some(Input), Some(Output)) => issueConnect(sink, source) - case (Some(Output), _) => throw UnwritableSinkException - case (_, None) => { + case (Input, Input) => issueConnect(sink, source) + case (Input, Output) => issueConnect(sink, source) + case (Output, _) => throw UnwritableSinkException + case (_, Internal) => { if (!(connectCompileOptions.dontAssumeDirectionality)) { issueConnect(sink, source) } else { throw UnreadableSourceException } } - case (None, _) => throw UnwritableSinkException + case (Internal, _) => throw UnwritableSinkException } } diff --git a/chiselFrontend/src/main/scala/chisel3/core/Mux.scala b/chiselFrontend/src/main/scala/chisel3/core/Mux.scala index f01c59ca..e4ef001f 100644 --- a/chiselFrontend/src/main/scala/chisel3/core/Mux.scala +++ b/chiselFrontend/src/main/scala/chisel3/core/Mux.scala @@ -26,10 +26,10 @@ object Mux { def do_apply[T <: Data](cond: Bool, con: T, alt: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = { - Binding.checkSynthesizable(cond, s"'cond' ($cond)") - Binding.checkSynthesizable(con, s"'con' ($con)") - Binding.checkSynthesizable(alt, s"'alt' ($alt)") + requireIsHardware(cond, "mux condition") + requireIsHardware(con, "mux true value") + requireIsHardware(alt, "mux false value") val d = cloneSupertype(Seq(con, alt), "Mux") pushOp(DefPrim(sourceInfo, d, MultiplexOp, cond.ref, con.ref, alt.ref)) } -} \ No newline at end of file +} diff --git a/chiselFrontend/src/main/scala/chisel3/core/Reg.scala b/chiselFrontend/src/main/scala/chisel3/core/Reg.scala index 12d0a939..3fdb3398 100644 --- a/chiselFrontend/src/main/scala/chisel3/core/Reg.scala +++ b/chiselFrontend/src/main/scala/chisel3/core/Reg.scala @@ -17,12 +17,12 @@ object Reg { */ def apply[T <: Data](t: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = { if (compileOptions.declaredTypeMustBeUnbound) { - Binding.checkUnbound(t, s"t ($t) must be unbound Type. Try using cloneType?") + requireIsChiselType(t, "reg type") } val reg = t.chiselCloneType val clock = Node(Builder.forcedClock) - Binding.bind(reg, RegBinder(Builder.forcedUserModule), "Error: t") + reg.bind(RegBinding(Builder.forcedUserModule)) pushCommand(DefReg(sourceInfo, reg, clock)) reg } @@ -40,7 +40,6 @@ object RegNext { }).asInstanceOf[T] val reg = Reg(model) - Binding.checkSynthesizable(next, s"'next' ($next)") // TODO: move into connect? reg := next reg @@ -57,7 +56,6 @@ object RegNext { }).asInstanceOf[T] val reg = RegInit(model, init) // TODO: this makes NO sense - Binding.checkSynthesizable(next, s"'next' ($next)") // TODO: move into connect? reg := next reg @@ -84,14 +82,14 @@ object RegInit { */ def apply[T <: Data](t: T, init: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = { if (compileOptions.declaredTypeMustBeUnbound) { - Binding.checkUnbound(t, s"t ($t) must be unbound Type. Try using cloneType?") + requireIsChiselType(t, "reg type") } val reg = t.chiselCloneType val clock = Node(Builder.forcedClock) val reset = Node(Builder.forcedReset) - Binding.bind(reg, RegBinder(Builder.forcedUserModule), "Error: t") - Binding.checkSynthesizable(init, s"'init' ($init)") + reg.bind(RegBinding(Builder.forcedUserModule)) + requireIsHardware(init, "reg initializer") pushCommand(DefRegInit(sourceInfo, reg, clock, reset, init.ref)) reg } diff --git a/chiselFrontend/src/main/scala/chisel3/core/UserModule.scala b/chiselFrontend/src/main/scala/chisel3/core/UserModule.scala index 666be4d0..5207ef04 100644 --- a/chiselFrontend/src/main/scala/chisel3/core/UserModule.scala +++ b/chiselFrontend/src/main/scala/chisel3/core/UserModule.scala @@ -64,21 +64,17 @@ abstract class UserModule(implicit moduleCompileOptions: CompileOptions) id._onModuleClose } - val firrtlPorts = for (port <- getModulePorts) yield { - // Port definitions need to know input or output at top-level. 'flipped' means Input. - val direction = if(Data.isFirrtlFlipped(port)) Direction.Input else Direction.Output - firrtl.Port(port, direction) - } + val firrtlPorts = getModulePorts map {port => Port(port, port.userDirection)} _firrtlPorts = Some(firrtlPorts) // Generate IO invalidation commands to initialize outputs as unused val invalidateCommands = getModulePorts map {port => DefInvalid(UnlocatableSourceInfo, port.ref)} - + val component = DefModule(this, name, firrtlPorts, invalidateCommands ++ getCommands) _component = Some(component) component } - + // There is no initialization to be done by default. private[core] def initializeInParent() {} } @@ -100,7 +96,7 @@ abstract class ImplicitModule(implicit moduleCompileOptions: CompileOptions) private[core] override def initializeInParent() { implicit val sourceInfo = UnlocatableSourceInfo - + for (port <- getModulePorts) { pushCommand(DefInvalid(sourceInfo, port.ref)) } @@ -122,7 +118,7 @@ abstract class LegacyModule(implicit moduleCompileOptions: CompileOptions) // These are to be phased out protected var override_clock: Option[Clock] = None protected var override_reset: Option[Bool] = None - + // _clock and _reset can be clock and reset in these 2ary constructors // once chisel2 compatibility issues are resolved @deprecated("Module constructor with override_clock and override_reset deprecated, use withClockAndReset", "chisel3") @@ -132,7 +128,7 @@ abstract class LegacyModule(implicit moduleCompileOptions: CompileOptions) this.override_clock = override_clock this.override_reset = override_reset } - + @deprecated("Module constructor with override _clock deprecated, use withClock", "chisel3") def this(_clock: Clock)(implicit moduleCompileOptions: CompileOptions) = this(Option(_clock), None)(moduleCompileOptions) @deprecated("Module constructor with override _reset deprecated, use withReset", "chisel3") @@ -174,7 +170,7 @@ abstract class LegacyModule(implicit moduleCompileOptions: CompileOptions) // Don't generate source info referencing parents inside a module, since this interferes with // module de-duplication in FIRRTL emission. implicit val sourceInfo = UnlocatableSourceInfo - + pushCommand(DefInvalid(sourceInfo, io.ref)) override_clock match { diff --git a/chiselFrontend/src/main/scala/chisel3/internal/Builder.scala b/chiselFrontend/src/main/scala/chisel3/internal/Builder.scala index 73556750..1d7a45e0 100644 --- a/chiselFrontend/src/main/scala/chisel3/internal/Builder.scala +++ b/chiselFrontend/src/main/scala/chisel3/internal/Builder.scala @@ -215,7 +215,7 @@ private[chisel3] object Builder { } def pushOp[T <: Data](cmd: DefPrim[T]): T = { // Bind each element of the returned Data to being a Op - Binding.bind(cmd.id, OpBinder(forcedUserModule), "Error: During op creation, fresh result") + cmd.id.bind(OpBinding(forcedUserModule)) pushCommand(cmd).id } diff --git a/chiselFrontend/src/main/scala/chisel3/internal/firrtl/IR.scala b/chiselFrontend/src/main/scala/chisel3/internal/firrtl/IR.scala index 18df7f51..cca368ef 100644 --- a/chiselFrontend/src/main/scala/chisel3/internal/firrtl/IR.scala +++ b/chiselFrontend/src/main/scala/chisel3/internal/firrtl/IR.scala @@ -266,7 +266,7 @@ case class BulkConnect(sourceInfo: SourceInfo, loc1: Node, loc2: Node) extends C case class Attach(sourceInfo: SourceInfo, locs: Seq[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 Port(id: Data, dir: Direction) +case class Port(id: Data, dir: UserDirection) case class Printf(sourceInfo: SourceInfo, clock: Arg, pable: Printable) extends Command abstract class Component extends Arg { def id: BaseModule diff --git a/src/main/scala/chisel3/compatibility.scala b/src/main/scala/chisel3/compatibility.scala index d64b3bb5..b9357bbd 100644 --- a/src/main/scala/chisel3/compatibility.scala +++ b/src/main/scala/chisel3/compatibility.scala @@ -12,21 +12,43 @@ package object Chisel { // scalastyle:ignore package.object.name import scala.annotation.compileTimeOnly implicit val defaultCompileOptions = chisel3.core.ExplicitCompileOptions.NotStrict - type Direction = chisel3.core.Direction - val INPUT = chisel3.core.Direction.Input - val OUTPUT = chisel3.core.Direction.Output - val NODIR = chisel3.core.Direction.Unspecified + abstract class Direction + case object INPUT extends Direction + case object OUTPUT extends Direction + case object NODIR extends Direction + object Flipped { def apply[T<:Data](target: T): T = chisel3.core.Flipped[T](target) } - // TODO: Possibly move the AddDirectionToData class here? + + implicit class AddDirectionToData[T<:Data](val target: T) extends AnyVal { + def asInput: T = chisel3.core.Input(target) + def asOutput: T = chisel3.core.Output(target) + def flip(): T = chisel3.core.Flipped(target) + } + implicit class AddDirMethodToData[T<:Data](val target: T) extends AnyVal { + import chisel3.core.{DataMirror, ActualDirection, UserDirection} def dir: Direction = { - target match { - case e: Element => e.dir - case _ => chisel3.core.Direction.Unspecified + DataMirror.isSynthesizable(target) match { + case true => target match { + case e: Element => DataMirror.directionOf(e) match { + case ActualDirection.Unspecified => NODIR + case ActualDirection.Output => OUTPUT + case ActualDirection.Input => INPUT + case dir => throw new RuntimeException(s"Unexpected element direction '$dir'") + } + case _ => NODIR + } + case false => DataMirror.userDirectionOf(target) match { // returns local direction only + case UserDirection.Unspecified => NODIR + case UserDirection.Input => INPUT + case UserDirection.Output => OUTPUT + case dir => throw new RuntimeException(s"Unexpected element direction '$dir'") + } } + } } @@ -34,7 +56,18 @@ package object Chisel { // scalastyle:ignore package.object.name type Data = chisel3.core.Data val Wire = chisel3.core.Wire - val Clock = chisel3.core.Clock + object Clock { + def apply(): Clock = new Clock + + def apply(dir: Direction): Clock = { + val result = apply() + dir match { + case INPUT => chisel3.core.Input(result) + case OUTPUT => chisel3.core.Output(result) + case _ => result + } + } + } type Clock = chisel3.core.Clock // Implicit conversion to allow fromBits because it's being deprecated in chisel3 @@ -94,9 +127,9 @@ package object Chisel { // scalastyle:ignore package.object.name def apply(dir: Direction, width: Width): UInt = { val result = apply(width) dir match { - case chisel3.core.Direction.Input => chisel3.core.Input(result) - case chisel3.core.Direction.Output => chisel3.core.Output(result) - case chisel3.core.Direction.Unspecified => result + case INPUT => chisel3.core.Input(result) + case OUTPUT => chisel3.core.Output(result) + case NODIR => result } } @@ -135,9 +168,9 @@ package object Chisel { // scalastyle:ignore package.object.name def apply(dir: Direction, width: Width): SInt = { val result = apply(width) dir match { - case chisel3.core.Direction.Input => chisel3.core.Input(result) - case chisel3.core.Direction.Output => chisel3.core.Output(result) - case chisel3.core.Direction.Unspecified => result + case INPUT => chisel3.core.Input(result) + case OUTPUT => chisel3.core.Output(result) + case NODIR => result } } } @@ -153,9 +186,9 @@ package object Chisel { // scalastyle:ignore package.object.name def apply(dir: Direction): Bool = { val result = apply() dir match { - case chisel3.core.Direction.Input => chisel3.core.Input(result) - case chisel3.core.Direction.Output => chisel3.core.Output(result) - case chisel3.core.Direction.Unspecified => result + case INPUT => chisel3.core.Input(result) + case OUTPUT => chisel3.core.Output(result) + case NODIR => result } } } @@ -210,6 +243,7 @@ package object Chisel { // scalastyle:ignore package.object.name } } } + val Module = chisel3.core.Module type Module = CompatibilityModule @@ -249,7 +283,6 @@ package object Chisel { // scalastyle:ignore package.object.name chisel3.core.Reg(t) } if (next ne null) { - Binding.checkSynthesizable(next, s"'next' ($next)") // TODO: move into connect? reg := next } reg diff --git a/src/main/scala/chisel3/internal/firrtl/Emitter.scala b/src/main/scala/chisel3/internal/firrtl/Emitter.scala index 16b39e35..593052c7 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.core.UserDirection import chisel3.experimental._ import chisel3.internal.sourceinfo.{NoSourceInfo, SourceLine} @@ -12,16 +13,50 @@ private[chisel3] object Emitter { private class Emitter(circuit: Circuit) { override def toString: String = res.toString - private def emitPort(e: Port): String = - s"${e.dir} ${e.id.getRef.name} : ${e.id.toType}" + private def emitPort(e: Port): String = { + val dir = e.dir match { + case UserDirection.Unspecified | UserDirection.Output => "output" + case UserDirection.Flip | UserDirection.Input => "input" + } + s"$dir ${e.id.getRef.name} : ${emitType(e.id)}" + } + + private def emitType(d: Data, clearDir: Boolean = false): String = d match { + case d: Clock => "Clock" + case d: UInt => s"UInt${d.width}" + case d: SInt => s"SInt${d.width}" + case d: FixedPoint => s"Fixed${d.width}${d.binaryPoint}" + case d: Analog => s"Analog${d.width}" + case d: Vec[_] => s"${emitType(d.sample_element, clearDir)}[${d.length}]" + case d: Record => { + val childClearDir = clearDir || + d.userDirection == UserDirection.Input || d.userDirection == UserDirection.Output + def eltPort(elt: Data): String = (childClearDir, firrtlUserDirOf(elt)) match { + case (true, _) => + s"${elt.getRef.name} : ${emitType(elt, true)}" + case (false, UserDirection.Unspecified | UserDirection.Output) => + s"${elt.getRef.name} : ${emitType(elt, false)}" + case (false, UserDirection.Flip | UserDirection.Input) => + s"flip ${elt.getRef.name} : ${emitType(elt, false)}" + } + d.elements.toIndexedSeq.reverse.map(e => eltPort(e._2)).mkString("{", ", ", "}") + } + } + + private def firrtlUserDirOf(d: Data): UserDirection = d match { + case d: Vec[_] => + UserDirection.fromParent(d.userDirection, firrtlUserDirOf(d.sample_element)) + case d => d.userDirection + } + private def emit(e: Command, ctx: Component): String = { 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} : ${e.id.toType}" - case e: DefReg => s"reg ${e.name} : ${e.id.toType}, ${e.clock.fullName(ctx)}" - case e: DefRegInit => s"reg ${e.name} : ${e.id.toType}, ${e.clock.fullName(ctx)} with : (reset => (${e.reset.fullName(ctx)}, ${e.init.fullName(ctx)}))" - case e: DefMemory => s"cmem ${e.name} : ${e.t.toType}[${e.size}]" - case e: DefSeqMemory => s"smem ${e.name} : ${e.t.toType}[${e.size}]" + 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)}))" + 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)}" case e: Connect => s"${e.loc.fullName(ctx)} <= ${e.exp.fullName(ctx)}" case e: BulkConnect => s"${e.loc1.fullName(ctx)} <- ${e.loc2.fullName(ctx)}" diff --git a/src/main/scala/chisel3/package.scala b/src/main/scala/chisel3/package.scala index 1899b2ec..d2f11f3d 100644 --- a/src/main/scala/chisel3/package.scala +++ b/src/main/scala/chisel3/package.scala @@ -11,11 +11,9 @@ package object chisel3 { // scalastyle:ignore package.object.name import util.BitPat - import chisel3.core.{Binding, FlippedBinder} import chisel3.util._ import chisel3.internal.firrtl.Port - type Direction = chisel3.core.Direction val Input = chisel3.core.Input val Output = chisel3.core.Output val Flipped = chisel3.core.Flipped @@ -25,6 +23,15 @@ package object chisel3 { // scalastyle:ignore package.object.name val Clock = chisel3.core.Clock type Clock = chisel3.core.Clock + implicit class AddDirectionToData[T<:Data](val target: T) extends AnyVal { + @deprecated("Input(Data) should be used over Data.asInput", "chisel3") + def asInput: T = Input(target) + @deprecated("Output(Data) should be used over Data.asOutput", "chisel3") + def asOutput: T = Output(target) + @deprecated("Flipped(Data) should be used over Data.flip", "chisel3") + def flip(): T = Flipped(target) + } + implicit class fromBitsable[T <: Data](val data: T) { import chisel3.core.CompileOptions import chisel3.internal.sourceinfo.SourceInfo @@ -34,7 +41,7 @@ package object chisel3 { // scalastyle:ignore package.object.name that.asTypeOf(data) } } - + type Aggregate = chisel3.core.Aggregate val Vec = chisel3.core.Vec type Vec[T <: Data] = chisel3.core.Vec[T] @@ -83,10 +90,6 @@ package object chisel3 { // scalastyle:ignore package.object.name @deprecated("use value.U(width.W)", "chisel3, will be removed by end of 2016") def apply(value: BigInt, width: Int): UInt = value.asUInt(width.W) - /** Create a UInt with a specified width - compatibility with Chisel2. */ - @deprecated("use UInt(width.W)", "chisel3, will be removed by end of 2016") - def apply(dir: Option[Direction] = None, width: Int): UInt = apply(width.W) - /** Create a UInt literal with inferred width.- compatibility with Chisel2. */ @deprecated("use value.U", "chisel3, will be removed by end of 2016") def apply(value: BigInt): UInt = value.asUInt @@ -188,7 +191,6 @@ package object chisel3 { // scalastyle:ignore package.object.name chisel3.core.Reg(t) } if (next ne null) { - Binding.checkSynthesizable(next, s"'next' ($next)") // TODO: move into connect? reg := next } reg @@ -284,10 +286,7 @@ package object chisel3 { // scalastyle:ignore package.object.name def do_=/= (that: BitPat)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = that =/= x // scalastyle:ignore method.name } - // Compatibility with existing code. - val INPUT = chisel3.core.Direction.Input - val OUTPUT = chisel3.core.Direction.Output - val NODIR = chisel3.core.Direction.Unspecified + type ChiselException = chisel3.internal.ChiselException // Debugger/Tester access to internal Chisel data structures and methods. @@ -295,7 +294,6 @@ package object chisel3 { // scalastyle:ignore package.object.name a.allElements } 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. * @@ -342,6 +340,12 @@ package object chisel3 { // scalastyle:ignore package.object.name type ChiselAnnotation = chisel3.core.ChiselAnnotation val ChiselAnnotation = chisel3.core.ChiselAnnotation + val DataMirror = chisel3.core.DataMirror + val requireIsHardware = chisel3.core.requireIsHardware + val requireIsChiselType = chisel3.core.requireIsChiselType + type Direction = chisel3.core.ActualDirection + val Direction = chisel3.core.ActualDirection + implicit class ChiselRange(val sc: StringContext) extends AnyVal { import scala.language.experimental.macros import internal.firrtl.NumericBound diff --git a/src/main/scala/chisel3/util/Decoupled.scala b/src/main/scala/chisel3/util/Decoupled.scala index 5de24728..b9e1e7ed 100644 --- a/src/main/scala/chisel3/util/Decoupled.scala +++ b/src/main/scala/chisel3/util/Decoupled.scala @@ -6,6 +6,7 @@ package chisel3.util import chisel3._ +import chisel3.experimental.{DataMirror, Direction} import chisel3.internal.naming._ // can't use chisel3_ version because of compile order /** An I/O Bundle containing 'valid' and 'ready' signals that handshake @@ -88,7 +89,7 @@ object Decoupled */ @chiselName def apply[T <: Data](irr: IrrevocableIO[T]): DecoupledIO[T] = { - require(irr.bits.flatten forall (_.dir == OUTPUT), "Only safe to cast produced Irrevocable bits to Decoupled.") + require(DataMirror.directionOf(irr.bits) == Direction.Output, "Only safe to cast produced Irrevocable bits to Decoupled.") val d = Wire(new DecoupledIO(irr.bits)) d.bits := irr.bits d.valid := irr.valid @@ -122,7 +123,7 @@ object Irrevocable * @note unsafe (and will error) on the consumer (output) side of an DecoupledIO */ def apply[T <: Data](dec: DecoupledIO[T]): IrrevocableIO[T] = { - require(dec.bits.flatten forall (_.dir == INPUT), "Only safe to cast consumed Decoupled bits to Irrevocable.") + require(DataMirror.directionOf(dec.bits) == Direction.Input, "Only safe to cast consumed Decoupled bits to Irrevocable.") val i = Wire(new IrrevocableIO(dec.bits)) dec.bits := i.bits dec.valid := i.valid diff --git a/src/test/scala/chiselTests/BlackBox.scala b/src/test/scala/chiselTests/BlackBox.scala index b3791fd9..983039c5 100644 --- a/src/test/scala/chiselTests/BlackBox.scala +++ b/src/test/scala/chiselTests/BlackBox.scala @@ -89,7 +89,7 @@ class BlackBoxConstant(value: Int) extends BlackBox( Map("VALUE" -> value, "WIDTH" -> log2Ceil(value + 1))) { require(value >= 0, "value must be a UInt!") val io = IO(new Bundle { - val out = UInt(log2Ceil(value + 1).W).asOutput + val out = Output(UInt(log2Ceil(value + 1).W)) }) } diff --git a/src/test/scala/chiselTests/CompatibilityInteroperabilitySpec.scala b/src/test/scala/chiselTests/CompatibilityInteroperabilitySpec.scala index 2baa6e48..c0538123 100644 --- a/src/test/scala/chiselTests/CompatibilityInteroperabilitySpec.scala +++ b/src/test/scala/chiselTests/CompatibilityInteroperabilitySpec.scala @@ -64,21 +64,21 @@ object Chisel3Components { } class Chisel3BundleModuleA extends Chisel3DriverModule(new Chisel3Bundle) - class Chisel3BundleModuleB extends Chisel3PassthroughModule((new Chisel3Bundle).flip) + class Chisel3BundleModuleB extends Chisel3PassthroughModule(Flipped(new Chisel3Bundle)) class Chisel3RecordModuleA extends Chisel3DriverModule(new Chisel3Record) - class Chisel3RecordModuleB extends Chisel3PassthroughModule((new Chisel3Record).flip) + class Chisel3RecordModuleB extends Chisel3PassthroughModule(Flipped(new Chisel3Record)) class Chisel3ModuleChiselBundleA extends Chisel3DriverModule(new ChiselBundle) - class Chisel3ModuleChiselBundleB extends Chisel3PassthroughModule((new ChiselBundle).flip) + class Chisel3ModuleChiselBundleB extends Chisel3PassthroughModule(Flipped(new ChiselBundle)) class Chisel3ModuleChiselRecordA extends Chisel3DriverModule(new ChiselRecord) - class Chisel3ModuleChiselRecordB extends Chisel3PassthroughModule((new ChiselRecord).flip) + class Chisel3ModuleChiselRecordB extends Chisel3PassthroughModule(Flipped(new ChiselRecord)) } class CompatibiltyInteroperabilitySpec extends ChiselFlatSpec { "Modules defined in the Chisel._" should "successfully bulk connect in chisel3._" in { - import chisel3._ - import chisel3.testers.BasicTester + import chisel3._ + import chisel3.testers.BasicTester import CompatibilityComponents._ assertTesterPasses(new BasicTester { @@ -96,8 +96,8 @@ class CompatibiltyInteroperabilitySpec extends ChiselFlatSpec { } "Moduless defined in the chisel3._" should "successfully bulk connect in Chisel._" in { - import Chisel._ - import chisel3.testers.BasicTester + import Chisel._ + import chisel3.testers.BasicTester import Chisel3Components._ assertTesterPasses(new BasicTester { @@ -116,8 +116,8 @@ class CompatibiltyInteroperabilitySpec extends ChiselFlatSpec { "Bundles defined in Chisel._" should "work in chisel3._ Modules" in { - import chisel3._ - import chisel3.testers.BasicTester + import chisel3._ + import chisel3.testers.BasicTester import Chisel3Components._ assertTesterPasses(new BasicTester { @@ -135,8 +135,8 @@ class CompatibiltyInteroperabilitySpec extends ChiselFlatSpec { } "Bundles defined in chisel3._" should "work in Chisel._ Modules" in { - import chisel3._ - import chisel3.testers.BasicTester + import chisel3._ + import chisel3.testers.BasicTester import CompatibilityComponents._ assertTesterPasses(new BasicTester { @@ -156,8 +156,8 @@ class CompatibiltyInteroperabilitySpec extends ChiselFlatSpec { "Similar Bundles defined in the chisel3._ and Chisel._" should "successfully bulk connect in chisel3._" in { - import chisel3._ - import chisel3.testers.BasicTester + import chisel3._ + import chisel3.testers.BasicTester import Chisel3Components._ import CompatibilityComponents._ @@ -187,8 +187,8 @@ class CompatibiltyInteroperabilitySpec extends ChiselFlatSpec { }) } they should "successfully bulk connect in Chisel._" in { - import Chisel._ - import chisel3.testers.BasicTester + import Chisel._ + import chisel3.testers.BasicTester import Chisel3Components._ import CompatibilityComponents._ diff --git a/src/test/scala/chiselTests/CompileOptionsTest.scala b/src/test/scala/chiselTests/CompileOptionsTest.scala index 102653af..110cf483 100644 --- a/src/test/scala/chiselTests/CompileOptionsTest.scala +++ b/src/test/scala/chiselTests/CompileOptionsTest.scala @@ -85,8 +85,8 @@ class CompileOptionsSpec extends ChiselFlatSpec { class RequireIOWrapModule extends Module { val io = IO(new Bundle { - val in = UInt(32.W).asInput - val out = Bool().asOutput + val in = Input(UInt(32.W)) + val out = Output(Bool()) }) io.out := io.in(1) } @@ -99,8 +99,8 @@ class CompileOptionsSpec extends ChiselFlatSpec { class RequireIOWrapModule extends Module { val io = new Bundle { - val in = UInt(32.W).asInput - val out = Bool().asOutput + val in = Input(UInt(32.W)) + val out = Output(Bool()) } io.out := io.in(1) } diff --git a/src/test/scala/chiselTests/Direction.scala b/src/test/scala/chiselTests/Direction.scala index 83ef7088..9b353840 100644 --- a/src/test/scala/chiselTests/Direction.scala +++ b/src/test/scala/chiselTests/Direction.scala @@ -8,21 +8,34 @@ import org.scalatest.matchers._ import org.scalatest.prop._ import chisel3.testers.BasicTester +class DirectionedBundle extends Bundle { + val in = Input(UInt(32.W)) + val out = Output(UInt(32.W)) +} + class DirectionHaver extends Module { val io = IO(new Bundle { val in = Input(UInt(32.W)) val out = Output(UInt(32.W)) + val inBundle = Input(new DirectionedBundle) // should override elements + val outBundle = Output(new DirectionedBundle) // should override elements }) } class GoodDirection extends DirectionHaver { io.out := 0.U + io.outBundle.in := 0.U + io.outBundle.out := 0.U } class BadDirection extends DirectionHaver { io.in := 0.U } +class BadSubDirection extends DirectionHaver { + io.inBundle.out := 0.U +} + class DirectionSpec extends ChiselPropSpec with Matchers { //TODO: In Chisel3 these are actually FIRRTL errors. Remove from tests? @@ -35,5 +48,8 @@ class DirectionSpec extends ChiselPropSpec with Matchers { a[Exception] should be thrownBy { elaborate(new BadDirection) } + a[Exception] should be thrownBy { + elaborate(new BadSubDirection) + } } } diff --git a/src/test/scala/chiselTests/FromBitsTester.scala b/src/test/scala/chiselTests/FromBitsTester.scala index 39d6a4fe..e916272f 100644 --- a/src/test/scala/chiselTests/FromBitsTester.scala +++ b/src/test/scala/chiselTests/FromBitsTester.scala @@ -5,10 +5,9 @@ package chiselTests import org.scalatest._ import chisel3._ -import chisel3.experimental.FixedPoint +import chisel3.experimental.{DataMirror, FixedPoint} import chisel3.testers.BasicTester import chisel3.util._ -import chisel3.core.DataMirror class FromBitsBundleTester extends BasicTester { class MultiTypeBundle extends Bundle { diff --git a/src/test/scala/chiselTests/MissingCloneBindingExceptionSpec.scala b/src/test/scala/chiselTests/MissingCloneBindingExceptionSpec.scala index bd1bade8..52ca418a 100644 --- a/src/test/scala/chiselTests/MissingCloneBindingExceptionSpec.scala +++ b/src/test/scala/chiselTests/MissingCloneBindingExceptionSpec.scala @@ -10,7 +10,7 @@ class MissingCloneBindingExceptionSpec extends ChiselFlatSpec with Matchers { import chisel3._ class TestIO(w: Int) extends Bundle { - val a = Vec(4, UInt(w.W)).asInput + val a = Input(Vec(4, UInt(w.W))) //override def cloneType = (new TestIO(w)).asInstanceOf[this.type] } diff --git a/src/test/scala/chiselTests/Module.scala b/src/test/scala/chiselTests/Module.scala index 4f043f0a..2ae8fa5e 100644 --- a/src/test/scala/chiselTests/Module.scala +++ b/src/test/scala/chiselTests/Module.scala @@ -60,7 +60,7 @@ class ModuleWireTester(c: ModuleWire) extends Tester(c) { class ModuleWhen extends Module { val io = IO(new Bundle { val s = new SimpleIO - val en = Bool() + val en = Output(Bool()) }) when(io.en) { val inc = Module(new PlusOne).io diff --git a/src/test/scala/chiselTests/RecordSpec.scala b/src/test/scala/chiselTests/RecordSpec.scala index 3358d506..d17ff9bd 100644 --- a/src/test/scala/chiselTests/RecordSpec.scala +++ b/src/test/scala/chiselTests/RecordSpec.scala @@ -24,7 +24,8 @@ trait RecordSpecUtils { override def cloneType = (new MyBundle).asInstanceOf[this.type] } // Useful for constructing types from CustomBundle - val fooBarType = new CustomBundle("foo" -> UInt(32.W), "bar" -> UInt(32.W)) + // This is a def because each call to this needs to return a new instance + def fooBarType = new CustomBundle("foo" -> UInt(32.W), "bar" -> UInt(32.W)) class MyModule(output: => Record, input: => Record) extends Module { val io = IO(new Bundle { diff --git a/src/test/scala/chiselTests/ReinterpretCast.scala b/src/test/scala/chiselTests/ReinterpretCast.scala index cd0d1fa9..61c351ab 100644 --- a/src/test/scala/chiselTests/ReinterpretCast.scala +++ b/src/test/scala/chiselTests/ReinterpretCast.scala @@ -5,10 +5,9 @@ package chiselTests import org.scalatest._ import chisel3._ -import chisel3.experimental.FixedPoint +import chisel3.experimental.{DataMirror, FixedPoint} import chisel3.testers.BasicTester import chisel3.util._ -import chisel3.core.DataMirror class AsBundleTester extends BasicTester { class MultiTypeBundle extends Bundle { -- cgit v1.2.3