From b2e004fb615a3c931d910a338b9faa99c1c975d7 Mon Sep 17 00:00:00 2001 From: Jack Koenig Date: Sun, 22 Mar 2020 18:11:25 -0700 Subject: Remove compile-internal from build.sbt This has the effect of causing the coreMacros and chiselFrontend projects to be published separately --- build.sbt | 26 ++++---------------------- 1 file changed, 4 insertions(+), 22 deletions(-) diff --git a/build.sbt b/build.sbt index be265361..d943ecfc 100644 --- a/build.sbt +++ b/build.sbt @@ -123,14 +123,10 @@ lazy val chiselSettings = Seq ( ) lazy val coreMacros = (project in file("coreMacros")). - settings(commonSettings: _*). - // Prevent separate JARs from being generated for coreMacros. - settings(skip in publish := true) + settings(commonSettings: _*) lazy val chiselFrontend = (project in file("chiselFrontend")). settings(commonSettings: _*). - // Prevent separate JARs from being generated for chiselFrontend. - settings(skip in publish := true). settings( scalacOptions := scalacOptions.value ++ Seq( "-deprecation", @@ -159,14 +155,8 @@ lazy val chisel = (project in file(".")). settings(commonSettings: _*). settings(chiselSettings: _*). settings(publishSettings: _*). - dependsOn(coreMacros % "compile-internal;test-internal"). - dependsOn(chiselFrontend % "compile-internal;test-internal"). - // We used to have to disable aggregation in general in order to suppress - // creation of subproject JARs (coreMacros and chiselFrontend) during publishing. - // This had the unfortunate side-effect of suppressing coverage tests and scaladoc generation in subprojects. - // The "skip in publish := true" setting in subproject settings seems to be - // sufficient to suppress subproject JAR creation, so we can restore - // general aggregation, and thus get coverage tests and scaladoc for subprojects. + dependsOn(coreMacros). + dependsOn(chiselFrontend). aggregate(coreMacros, chiselFrontend). settings( scalacOptions in Test ++= Seq("-language:reflectiveCalls"), @@ -189,13 +179,5 @@ lazy val chisel = (project in file(".")). } s"https://github.com/freechipsproject/chisel3/tree/$branch/€{FILE_PATH}.scala" } - ), - // Include macro classes, resources, and sources main JAR since we don't create subproject JARs. - mappings in (Compile, packageBin) ++= (mappings in (coreMacros, Compile, packageBin)).value, - mappings in (Compile, packageSrc) ++= (mappings in (coreMacros, Compile, packageSrc)).value, - mappings in (Compile, packageBin) ++= (mappings in (chiselFrontend, Compile, packageBin)).value, - mappings in (Compile, packageSrc) ++= (mappings in (chiselFrontend, Compile, packageSrc)).value, - // Export the packaged JAR so projects that depend directly on Chisel project (rather than the - // published artifact) also see the stuff in coreMacros and chiselFrontend. - exportJars := true + ) ) -- cgit v1.2.3 From fbf5e6f1a0e8bf535d465b748ad554575fe62156 Mon Sep 17 00:00:00 2001 From: Jack Koenig Date: Sun, 22 Mar 2020 18:13:58 -0700 Subject: Rename subprojects to more canonical names * Rename coreMacros to macros * Rename chiselFrontend to core Also make each subproject publish with "chisel3-" as a prefix --- build.sbt | 14 +- .../src/main/scala/chisel3/Aggregate.scala | 1016 --------- .../src/main/scala/chisel3/Annotation.scala | 94 - chiselFrontend/src/main/scala/chisel3/Assert.scala | 92 - chiselFrontend/src/main/scala/chisel3/Attach.scala | 46 - chiselFrontend/src/main/scala/chisel3/Bits.scala | 2289 -------------------- .../src/main/scala/chisel3/BlackBox.scala | 181 -- .../src/main/scala/chisel3/BoolFactory.scala | 22 - chiselFrontend/src/main/scala/chisel3/Clock.scala | 43 - .../src/main/scala/chisel3/CompileOptions.scala | 80 - chiselFrontend/src/main/scala/chisel3/Data.scala | 729 ------- .../src/main/scala/chisel3/Element.scala | 62 - chiselFrontend/src/main/scala/chisel3/Mem.scala | 216 -- chiselFrontend/src/main/scala/chisel3/Module.scala | 405 ---- .../src/main/scala/chisel3/ModuleAspect.scala | 26 - .../src/main/scala/chisel3/MultiClock.scala | 70 - chiselFrontend/src/main/scala/chisel3/Mux.scala | 50 - chiselFrontend/src/main/scala/chisel3/Num.scala | 308 --- .../src/main/scala/chisel3/Printable.scala | 178 -- chiselFrontend/src/main/scala/chisel3/Printf.scala | 102 - .../src/main/scala/chisel3/RawModule.scala | 233 -- chiselFrontend/src/main/scala/chisel3/Reg.scala | 195 -- .../src/main/scala/chisel3/SIntFactory.scala | 25 - .../src/main/scala/chisel3/SeqUtils.scala | 128 -- .../src/main/scala/chisel3/StrongEnum.scala | 343 --- .../src/main/scala/chisel3/UIntFactory.scala | 47 - chiselFrontend/src/main/scala/chisel3/When.scala | 85 - .../src/main/scala/chisel3/aop/Aspect.scala | 40 - .../src/main/scala/chisel3/core/package.scala | 288 --- .../src/main/scala/chisel3/dontTouch.scala | 37 - .../main/scala/chisel3/experimental/Analog.scala | 85 - .../main/scala/chisel3/experimental/package.scala | 140 -- .../main/scala/chisel3/internal/BiConnect.scala | 333 --- .../src/main/scala/chisel3/internal/Binding.scala | 114 - .../src/main/scala/chisel3/internal/Builder.scala | 452 ---- .../src/main/scala/chisel3/internal/Error.scala | 213 -- .../main/scala/chisel3/internal/MonoConnect.scala | 264 --- .../src/main/scala/chisel3/internal/Namer.scala | 154 -- .../main/scala/chisel3/internal/SourceInfo.scala | 61 - .../scala/chisel3/internal/firrtl/Converter.scala | 275 --- .../main/scala/chisel3/internal/firrtl/IR.scala | 750 ------- .../src/main/scala/chisel3/package.scala | 229 -- core/src/main/scala/chisel3/Aggregate.scala | 1016 +++++++++ core/src/main/scala/chisel3/Annotation.scala | 94 + core/src/main/scala/chisel3/Assert.scala | 92 + core/src/main/scala/chisel3/Attach.scala | 46 + core/src/main/scala/chisel3/Bits.scala | 2289 ++++++++++++++++++++ core/src/main/scala/chisel3/BlackBox.scala | 181 ++ core/src/main/scala/chisel3/BoolFactory.scala | 22 + core/src/main/scala/chisel3/Clock.scala | 43 + core/src/main/scala/chisel3/CompileOptions.scala | 80 + core/src/main/scala/chisel3/Data.scala | 729 +++++++ core/src/main/scala/chisel3/Element.scala | 62 + core/src/main/scala/chisel3/Mem.scala | 216 ++ core/src/main/scala/chisel3/Module.scala | 405 ++++ core/src/main/scala/chisel3/ModuleAspect.scala | 26 + core/src/main/scala/chisel3/MultiClock.scala | 70 + core/src/main/scala/chisel3/Mux.scala | 50 + core/src/main/scala/chisel3/Num.scala | 308 +++ core/src/main/scala/chisel3/Printable.scala | 178 ++ core/src/main/scala/chisel3/Printf.scala | 102 + core/src/main/scala/chisel3/RawModule.scala | 233 ++ core/src/main/scala/chisel3/Reg.scala | 195 ++ core/src/main/scala/chisel3/SIntFactory.scala | 25 + core/src/main/scala/chisel3/SeqUtils.scala | 128 ++ core/src/main/scala/chisel3/StrongEnum.scala | 343 +++ core/src/main/scala/chisel3/UIntFactory.scala | 47 + core/src/main/scala/chisel3/When.scala | 85 + core/src/main/scala/chisel3/aop/Aspect.scala | 40 + core/src/main/scala/chisel3/core/package.scala | 288 +++ core/src/main/scala/chisel3/dontTouch.scala | 37 + .../main/scala/chisel3/experimental/Analog.scala | 85 + .../main/scala/chisel3/experimental/package.scala | 140 ++ .../main/scala/chisel3/internal/BiConnect.scala | 333 +++ core/src/main/scala/chisel3/internal/Binding.scala | 114 + core/src/main/scala/chisel3/internal/Builder.scala | 452 ++++ core/src/main/scala/chisel3/internal/Error.scala | 213 ++ .../main/scala/chisel3/internal/MonoConnect.scala | 264 +++ core/src/main/scala/chisel3/internal/Namer.scala | 154 ++ .../main/scala/chisel3/internal/SourceInfo.scala | 61 + .../scala/chisel3/internal/firrtl/Converter.scala | 275 +++ .../main/scala/chisel3/internal/firrtl/IR.scala | 750 +++++++ core/src/main/scala/chisel3/package.scala | 229 ++ .../src/main/scala/chisel3/SourceInfoDoc.scala | 38 - .../scala/chisel3/internal/RangeTransform.scala | 197 -- .../internal/RuntimeDeprecationTransform.scala | 42 - .../internal/naming/NamingAnnotations.scala | 205 -- .../internal/sourceinfo/SourceInfoTransform.scala | 186 -- macros/src/main/scala/chisel3/SourceInfoDoc.scala | 38 + .../scala/chisel3/internal/RangeTransform.scala | 197 ++ .../internal/RuntimeDeprecationTransform.scala | 42 + .../internal/naming/NamingAnnotations.scala | 205 ++ .../internal/sourceinfo/SourceInfoTransform.scala | 186 ++ 93 files changed, 11176 insertions(+), 11174 deletions(-) delete mode 100644 chiselFrontend/src/main/scala/chisel3/Aggregate.scala delete mode 100644 chiselFrontend/src/main/scala/chisel3/Annotation.scala delete mode 100644 chiselFrontend/src/main/scala/chisel3/Assert.scala delete mode 100644 chiselFrontend/src/main/scala/chisel3/Attach.scala delete mode 100644 chiselFrontend/src/main/scala/chisel3/Bits.scala delete mode 100644 chiselFrontend/src/main/scala/chisel3/BlackBox.scala delete mode 100644 chiselFrontend/src/main/scala/chisel3/BoolFactory.scala delete mode 100644 chiselFrontend/src/main/scala/chisel3/Clock.scala delete mode 100644 chiselFrontend/src/main/scala/chisel3/CompileOptions.scala delete mode 100644 chiselFrontend/src/main/scala/chisel3/Data.scala delete mode 100644 chiselFrontend/src/main/scala/chisel3/Element.scala delete mode 100644 chiselFrontend/src/main/scala/chisel3/Mem.scala delete mode 100644 chiselFrontend/src/main/scala/chisel3/Module.scala delete mode 100644 chiselFrontend/src/main/scala/chisel3/ModuleAspect.scala delete mode 100644 chiselFrontend/src/main/scala/chisel3/MultiClock.scala delete mode 100644 chiselFrontend/src/main/scala/chisel3/Mux.scala delete mode 100644 chiselFrontend/src/main/scala/chisel3/Num.scala delete mode 100644 chiselFrontend/src/main/scala/chisel3/Printable.scala delete mode 100644 chiselFrontend/src/main/scala/chisel3/Printf.scala delete mode 100644 chiselFrontend/src/main/scala/chisel3/RawModule.scala delete mode 100644 chiselFrontend/src/main/scala/chisel3/Reg.scala delete mode 100644 chiselFrontend/src/main/scala/chisel3/SIntFactory.scala delete mode 100644 chiselFrontend/src/main/scala/chisel3/SeqUtils.scala delete mode 100644 chiselFrontend/src/main/scala/chisel3/StrongEnum.scala delete mode 100644 chiselFrontend/src/main/scala/chisel3/UIntFactory.scala delete mode 100644 chiselFrontend/src/main/scala/chisel3/When.scala delete mode 100644 chiselFrontend/src/main/scala/chisel3/aop/Aspect.scala delete mode 100644 chiselFrontend/src/main/scala/chisel3/core/package.scala delete mode 100644 chiselFrontend/src/main/scala/chisel3/dontTouch.scala delete mode 100644 chiselFrontend/src/main/scala/chisel3/experimental/Analog.scala delete mode 100644 chiselFrontend/src/main/scala/chisel3/experimental/package.scala delete mode 100644 chiselFrontend/src/main/scala/chisel3/internal/BiConnect.scala delete mode 100644 chiselFrontend/src/main/scala/chisel3/internal/Binding.scala delete mode 100644 chiselFrontend/src/main/scala/chisel3/internal/Builder.scala delete mode 100644 chiselFrontend/src/main/scala/chisel3/internal/Error.scala delete mode 100644 chiselFrontend/src/main/scala/chisel3/internal/MonoConnect.scala delete mode 100644 chiselFrontend/src/main/scala/chisel3/internal/Namer.scala delete mode 100644 chiselFrontend/src/main/scala/chisel3/internal/SourceInfo.scala delete mode 100644 chiselFrontend/src/main/scala/chisel3/internal/firrtl/Converter.scala delete mode 100644 chiselFrontend/src/main/scala/chisel3/internal/firrtl/IR.scala delete mode 100644 chiselFrontend/src/main/scala/chisel3/package.scala create mode 100644 core/src/main/scala/chisel3/Aggregate.scala create mode 100644 core/src/main/scala/chisel3/Annotation.scala create mode 100644 core/src/main/scala/chisel3/Assert.scala create mode 100644 core/src/main/scala/chisel3/Attach.scala create mode 100644 core/src/main/scala/chisel3/Bits.scala create mode 100644 core/src/main/scala/chisel3/BlackBox.scala create mode 100644 core/src/main/scala/chisel3/BoolFactory.scala create mode 100644 core/src/main/scala/chisel3/Clock.scala create mode 100644 core/src/main/scala/chisel3/CompileOptions.scala create mode 100644 core/src/main/scala/chisel3/Data.scala create mode 100644 core/src/main/scala/chisel3/Element.scala create mode 100644 core/src/main/scala/chisel3/Mem.scala create mode 100644 core/src/main/scala/chisel3/Module.scala create mode 100644 core/src/main/scala/chisel3/ModuleAspect.scala create mode 100644 core/src/main/scala/chisel3/MultiClock.scala create mode 100644 core/src/main/scala/chisel3/Mux.scala create mode 100644 core/src/main/scala/chisel3/Num.scala create mode 100644 core/src/main/scala/chisel3/Printable.scala create mode 100644 core/src/main/scala/chisel3/Printf.scala create mode 100644 core/src/main/scala/chisel3/RawModule.scala create mode 100644 core/src/main/scala/chisel3/Reg.scala create mode 100644 core/src/main/scala/chisel3/SIntFactory.scala create mode 100644 core/src/main/scala/chisel3/SeqUtils.scala create mode 100644 core/src/main/scala/chisel3/StrongEnum.scala create mode 100644 core/src/main/scala/chisel3/UIntFactory.scala create mode 100644 core/src/main/scala/chisel3/When.scala create mode 100644 core/src/main/scala/chisel3/aop/Aspect.scala create mode 100644 core/src/main/scala/chisel3/core/package.scala create mode 100644 core/src/main/scala/chisel3/dontTouch.scala create mode 100644 core/src/main/scala/chisel3/experimental/Analog.scala create mode 100644 core/src/main/scala/chisel3/experimental/package.scala create mode 100644 core/src/main/scala/chisel3/internal/BiConnect.scala create mode 100644 core/src/main/scala/chisel3/internal/Binding.scala create mode 100644 core/src/main/scala/chisel3/internal/Builder.scala create mode 100644 core/src/main/scala/chisel3/internal/Error.scala create mode 100644 core/src/main/scala/chisel3/internal/MonoConnect.scala create mode 100644 core/src/main/scala/chisel3/internal/Namer.scala create mode 100644 core/src/main/scala/chisel3/internal/SourceInfo.scala create mode 100644 core/src/main/scala/chisel3/internal/firrtl/Converter.scala create mode 100644 core/src/main/scala/chisel3/internal/firrtl/IR.scala create mode 100644 core/src/main/scala/chisel3/package.scala delete mode 100644 coreMacros/src/main/scala/chisel3/SourceInfoDoc.scala delete mode 100644 coreMacros/src/main/scala/chisel3/internal/RangeTransform.scala delete mode 100644 coreMacros/src/main/scala/chisel3/internal/RuntimeDeprecationTransform.scala delete mode 100644 coreMacros/src/main/scala/chisel3/internal/naming/NamingAnnotations.scala delete mode 100644 coreMacros/src/main/scala/chisel3/internal/sourceinfo/SourceInfoTransform.scala create mode 100644 macros/src/main/scala/chisel3/SourceInfoDoc.scala create mode 100644 macros/src/main/scala/chisel3/internal/RangeTransform.scala create mode 100644 macros/src/main/scala/chisel3/internal/RuntimeDeprecationTransform.scala create mode 100644 macros/src/main/scala/chisel3/internal/naming/NamingAnnotations.scala create mode 100644 macros/src/main/scala/chisel3/internal/sourceinfo/SourceInfoTransform.scala diff --git a/build.sbt b/build.sbt index d943ecfc..64520fa1 100644 --- a/build.sbt +++ b/build.sbt @@ -122,12 +122,14 @@ lazy val chiselSettings = Seq ( } ) -lazy val coreMacros = (project in file("coreMacros")). +lazy val macros = (project in file("macros")). + settings(name := "chisel3-macros"). settings(commonSettings: _*) -lazy val chiselFrontend = (project in file("chiselFrontend")). +lazy val core = (project in file("core")). settings(commonSettings: _*). settings( + name := "chisel3-core", scalacOptions := scalacOptions.value ++ Seq( "-deprecation", "-explaintypes", @@ -139,7 +141,7 @@ lazy val chiselFrontend = (project in file("chiselFrontend")). // "-Xlint:missing-interpolator" ) ). - dependsOn(coreMacros) + dependsOn(macros) // This will always be the root project, even if we are a sub-project. lazy val root = RootProject(file(".")) @@ -155,9 +157,9 @@ lazy val chisel = (project in file(".")). settings(commonSettings: _*). settings(chiselSettings: _*). settings(publishSettings: _*). - dependsOn(coreMacros). - dependsOn(chiselFrontend). - aggregate(coreMacros, chiselFrontend). + dependsOn(macros). + dependsOn(core). + aggregate(macros, core). settings( scalacOptions in Test ++= Seq("-language:reflectiveCalls"), scalacOptions in Compile in doc ++= Seq( diff --git a/chiselFrontend/src/main/scala/chisel3/Aggregate.scala b/chiselFrontend/src/main/scala/chisel3/Aggregate.scala deleted file mode 100644 index 6c1e8dfb..00000000 --- a/chiselFrontend/src/main/scala/chisel3/Aggregate.scala +++ /dev/null @@ -1,1016 +0,0 @@ -// See LICENSE for license details. - -package chisel3 - -import scala.collection.immutable.ListMap -import scala.collection.mutable.{HashSet, LinkedHashMap} -import scala.language.experimental.macros - -import chisel3.experimental.BaseModule -import chisel3.experimental.BundleLiteralException -import chisel3.experimental.EnumType -import chisel3.internal._ -import chisel3.internal.Builder.pushCommand -import chisel3.internal.firrtl._ -import chisel3.internal.sourceinfo._ - -class AliasedAggregateFieldException(message: String) extends ChiselException(message) - -/** An abstract class for data types that solely consist of (are an aggregate - * of) other Data objects. - */ -sealed abstract class Aggregate extends Data { - private[chisel3] override def bind(target: Binding, parentDirection: SpecifiedDirection) { // scalastyle:ignore cyclomatic.complexity line.size.limit - binding = target - - val resolvedDirection = SpecifiedDirection.fromParent(parentDirection, specifiedDirection) - val duplicates = getElements.groupBy(identity).collect { case (x, elts) if elts.size > 1 => x } - if (!duplicates.isEmpty) { - throw new AliasedAggregateFieldException(s"Aggregate $this contains aliased fields $duplicates") - } - for (child <- getElements) { - child.bind(ChildBinding(this), resolvedDirection) - } - - // Check that children obey the directionality rules. - val childDirections = getElements.map(_.direction).toSet - ActualDirection.Empty - direction = ActualDirection.fromChildren(childDirections, resolvedDirection) match { - case Some(dir) => dir - case None => - val childWithDirections = getElements zip getElements.map(_.direction) - throw MixedDirectionAggregateException( - s"Aggregate '$this' can't have elements that are both directioned and undirectioned: $childWithDirections") - } - } - - override def litOption: Option[BigInt] = None // TODO implement me - - /** Returns a Seq of the immediate contents of this Aggregate, in order. - */ - def getElements: Seq[Data] - - private[chisel3] def width: Width = getElements.map(_.width).foldLeft(0.W)(_ + _) - private[chisel3] def legacyConnect(that: Data)(implicit sourceInfo: SourceInfo): Unit = { - // If the source is a DontCare, generate a DefInvalid for the sink, - // otherwise, issue a Connect. - if (that == DontCare) { - pushCommand(DefInvalid(sourceInfo, Node(this))) - } else { - pushCommand(BulkConnect(sourceInfo, Node(this), Node(that))) - } - } - - override def do_asUInt(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = { - SeqUtils.do_asUInt(flatten.map(_.asUInt())) - } - private[chisel3] override def connectFromBits(that: Bits)(implicit sourceInfo: SourceInfo, - compileOptions: CompileOptions): Unit = { - var i = 0 - val bits = if (that.isLit) that else WireDefault(UInt(this.width), that) // handles width padding - for (x <- flatten) { - val fieldWidth = x.getWidth - if (fieldWidth > 0) { - x.connectFromBits(bits(i + fieldWidth - 1, i)) - i += fieldWidth - } else { - // There's a zero-width field in this bundle. - // Zero-width fields can't really be assigned to, but the frontend complains if there are uninitialized fields, - // so we assign it to DontCare. We can't use connectFromBits() on DontCare, so use := instead. - x := DontCare - } - } - } -} - -trait VecFactory extends SourceInfoDoc { - /** Creates a new [[Vec]] with `n` entries of the specified data type. - * - * @note elements are NOT assigned by default and have no value - */ - def apply[T <: Data](n: Int, gen: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Vec[T] = { - if (compileOptions.declaredTypeMustBeUnbound) { - requireIsChiselType(gen, "vec type") - } - new Vec(gen.cloneTypeFull, n) - } - - /** Truncate an index to implement modulo-power-of-2 addressing. */ - private[chisel3] def truncateIndex(idx: UInt, n: BigInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = { // scalastyle:ignore line.size.limit - // scalastyle:off if.brace - val w = (n-1).bitLength - if (n <= 1) 0.U - else if (idx.width.known && idx.width.get <= w) idx - else if (idx.width.known) idx(w-1,0) - else (idx | 0.U(w.W))(w-1,0) - // scalastyle:on if.brace - } -} - -// scalastyle:off line.size.limit -/** A vector (array) of [[Data]] elements. Provides hardware versions of various - * collection transformation functions found in software array implementations. - * - * Careful consideration should be given over the use of [[Vec]] vs - * [[scala.collection.immutable.Seq Seq]] or some other Scala collection. In general [[Vec]] only - * needs to be used when there is a need to express the hardware collection in a [[Reg]] or IO - * [[Bundle]] or when access to elements of the array is indexed via a hardware signal. - * - * Example of indexing into a [[Vec]] using a hardware address and where the [[Vec]] is defined in - * an IO [[Bundle]] - * - * {{{ - * val io = IO(new Bundle { - * val in = Input(Vec(20, UInt(16.W))) - * val addr = Input(UInt(5.W)) - * val out = Output(UInt(16.W)) - * }) - * io.out := io.in(io.addr) - * }}} - * - * @tparam T type of elements - * - * @note - * - when multiple conflicting assignments are performed on a Vec element, the last one takes effect (unlike Mem, where the result is undefined) - * - Vecs, unlike classes in Scala's collection library, are propagated intact to FIRRTL as a vector type, which may make debugging easier - */ -// scalastyle:on line.size.limit -sealed class Vec[T <: Data] private[chisel3] (gen: => T, val length: Int) - extends Aggregate with VecLike[T] { - override def toString: String = { - val elementType = sample_element.cloneType - s"$elementType[$length]$bindingToString" - } - - private[chisel3] override def typeEquivalent(that: Data): Boolean = that match { - case that: Vec[T] => - this.length == that.length && - (this.sample_element typeEquivalent that.sample_element) - case _ => false - } - - private[chisel3] override def bind(target: Binding, parentDirection: SpecifiedDirection) { - binding = target - - val resolvedDirection = SpecifiedDirection.fromParent(parentDirection, specifiedDirection) - sample_element.bind(SampleElementBinding(this), resolvedDirection) - for (child <- getElements) { // assume that all children are the same - child.bind(ChildBinding(this), resolvedDirection) - } - - // Since all children are the same, we can just use the sample_element rather than all children - // .get is safe because None means mixed directions, we only pass 1 so that's not possible - direction = ActualDirection.fromChildren(Set(sample_element.direction), resolvedDirection).get - } - - // Note: the constructor takes a gen() function instead of a Seq to enforce - // that all elements must be the same and because it makes FIRRTL generation - // simpler. - private lazy val self: Seq[T] = { - val _self = Vector.fill(length)(gen) - for ((elt, i) <- _self.zipWithIndex) - elt.setRef(this, i) - _self - } - - /** - * sample_element 'tracks' all changes to the elements. - * For consistency, sample_element is always used for creating dynamically - * indexed ports and outputing the FIRRTL type. - * - * Needed specifically for the case when the Vec is length 0. - */ - 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 - // to deprecate allElements in favor of dispatched functions to Data or - // a pattern matched recursive descent - private[chisel3] final override def allElements: Seq[Element] = - (sample_element +: self).flatMap(_.allElements) - - /** Strong bulk connect, assigning elements in this Vec from elements in a Seq. - * - * @note the length of this Vec must match the length of the input Seq - */ - def <> (that: Seq[T])(implicit sourceInfo: SourceInfo, moduleCompileOptions: CompileOptions): Unit = { - if (this.length != that.length) { - Builder.error("Vec and Seq being bulk connected have different lengths!") - } - for ((a, b) <- this zip that) - a <> b - } - - // TODO: eliminate once assign(Seq) isn't ambiguous with assign(Data) since Vec extends Seq and Data - def <> (that: Vec[T])(implicit sourceInfo: SourceInfo, moduleCompileOptions: CompileOptions): Unit = this bulkConnect that.asInstanceOf[Data] // scalastyle:ignore line.size.limit - - /** Strong bulk connect, assigning elements in this Vec from elements in a Seq. - * - * @note the length of this Vec must match the length of the input Seq - */ - def := (that: Seq[T])(implicit sourceInfo: SourceInfo, moduleCompileOptions: CompileOptions): Unit = { - require(this.length == that.length, s"Cannot assign to a Vec of length ${this.length} from a Seq of different length ${that.length}") - for ((a, b) <- this zip that) - a := b - } - - // TODO: eliminate once assign(Seq) isn't ambiguous with assign(Data) since Vec extends Seq and Data - def := (that: Vec[T])(implicit sourceInfo: SourceInfo, moduleCompileOptions: CompileOptions): Unit = this connect that - - /** Creates a dynamically indexed read or write accessor into the array. - */ - override def apply(p: UInt): T = macro CompileOptionsTransform.pArg - - /** @group SourceInfoTransformMacro */ - def do_apply(p: UInt)(implicit compileOptions: CompileOptions): T = { - requireIsHardware(this, "vec") - requireIsHardware(p, "vec index") - val port = gen - - // 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 => SpecifiedDirection.Input - case ActualDirection.Output => SpecifiedDirection.Output - case ActualDirection.Bidirectional(ActualDirection.Default) | ActualDirection.Unspecified => - SpecifiedDirection.Unspecified - case ActualDirection.Bidirectional(ActualDirection.Flipped) => SpecifiedDirection.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 - } - - /** Creates a statically indexed read or write accessor into the array. - */ - def apply(idx: Int): T = self(idx) - - override def cloneType: this.type = { - new Vec(gen.cloneTypeFull, length).asInstanceOf[this.type] - } - - override def getElements: Seq[Data] = - (0 until length).map(apply(_)) - - /** Default "pretty-print" implementation - * Analogous to printing a Seq - * Results in "Vec(elt0, elt1, ...)" - */ - def toPrintable: Printable = { - // scalastyle:off if.brace - val elts = - if (length == 0) List.empty[Printable] - else self flatMap (e => List(e.toPrintable, PString(", "))) dropRight 1 - // scalastyle:on if.brace - PString("Vec(") + Printables(elts) + PString(")") - } - - /** A reduce operation in a tree like structure instead of sequentially - * @example An adder tree - * {{{ - * val sumOut = inputNums.reduceTree((a: T, b: T) => (a + b)) - * }}} - */ - def reduceTree(redOp: (T, T) => T): T = macro VecTransform.reduceTreeDefault - - /** A reduce operation in a tree like structure instead of sequentially - * @example A pipelined adder tree - * {{{ - * val sumOut = inputNums.reduceTree( - * (a: T, b: T) => RegNext(a + b), - * (a: T) => RegNext(a) - * ) - * }}} - */ - def reduceTree(redOp: (T, T) => T, layerOp: (T) => T): T = macro VecTransform.reduceTree - - def do_reduceTree(redOp: (T, T) => T, layerOp: (T) => T = (x: T) => x) - (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions) : T = { - require(!isEmpty, "Cannot apply reduction on a vec of size 0") - var curLayer = this - while (curLayer.length > 1) { - curLayer = VecInit(curLayer.grouped(2).map( x => - if (x.length == 1) layerOp(x(0)) else redOp(x(0), x(1)) - ).toSeq) - } - curLayer(0) - } -} - -object VecInit extends SourceInfoDoc { - /** Creates a new [[Vec]] composed of elements of the input Seq of [[Data]] - * nodes. - * - * @note input elements should be of the same type (this is checked at the - * FIRRTL level, but not at the Scala / Chisel level) - * @note the width of all output elements is the width of the largest input - * element - * @note output elements are connected from the input elements - */ - def apply[T <: Data](elts: Seq[T]): Vec[T] = macro VecTransform.apply_elts - - /** @group SourceInfoTransformMacro */ - def do_apply[T <: Data](elts: Seq[T])(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Vec[T] = { - // REVIEW TODO: this should be removed in favor of the apply(elts: T*) - // varargs constructor, which is more in line with the style of the Scala - // collection API. However, a deprecation phase isn't possible, since - // changing apply(elt0, elts*) to apply(elts*) causes a function collision - // with apply(Seq) after type erasure. Workarounds by either introducing a - // DummyImplicit or additional type parameter will break some code. - - // Check that types are homogeneous. Width mismatch for Elements is safe. - require(!elts.isEmpty) - elts.foreach(requireIsHardware(_, "vec element")) - - val vec = Wire(Vec(elts.length, cloneSupertype(elts, "Vec"))) - - // 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 - } - - /** Creates a new [[Vec]] composed of the input [[Data]] nodes. - * - * @note input elements should be of the same type (this is checked at the - * FIRRTL level, but not at the Scala / Chisel level) - * @note the width of all output elements is the width of the largest input - * element - * @note output elements are connected from the input elements - */ - def apply[T <: Data](elt0: T, elts: T*): Vec[T] = macro VecTransform.apply_elt0 - - /** @group SourceInfoTransformMacro */ - def do_apply[T <: Data](elt0: T, elts: T*)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Vec[T] = - apply(elt0 +: elts.toSeq) - - /** Creates a new [[Vec]] of length `n` composed of the results of the given - * function applied over a range of integer values starting from 0. - * - * @param n number of elements in the vector (the function is applied from - * 0 to `n-1`) - * @param gen function that takes in an Int (the index) and returns a - * [[Data]] that becomes the output element - */ - def tabulate[T <: Data](n: Int)(gen: (Int) => T): Vec[T] = macro VecTransform.tabulate - - /** @group SourceInfoTransformMacro */ - def do_tabulate[T <: Data](n: Int)(gen: (Int) => T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Vec[T] = // scalastyle:ignore line.size.limit - apply((0 until n).map(i => gen(i))) -} - -/** A trait for [[Vec]]s containing common hardware generators for collection - * operations. - */ -trait VecLike[T <: Data] extends collection.IndexedSeq[T] with HasId with SourceInfoDoc { - def apply(p: UInt): T = macro CompileOptionsTransform.pArg - - /** @group SourceInfoTransformMacro */ - def do_apply(p: UInt)(implicit compileOptions: CompileOptions): T - - // IndexedSeq has its own hashCode/equals that we must not use - override def hashCode: Int = super[HasId].hashCode - override def equals(that: Any): Boolean = super[HasId].equals(that) - - /** Outputs true if p outputs true for every element. - */ - def forall(p: T => Bool): Bool = macro SourceInfoTransform.pArg - - /** @group SourceInfoTransformMacro */ - def do_forall(p: T => Bool)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = - (this map p).fold(true.B)(_ && _) - - /** Outputs true if p outputs true for at least one element. - */ - def exists(p: T => Bool): Bool = macro SourceInfoTransform.pArg - - /** @group SourceInfoTransformMacro */ - def do_exists(p: T => Bool)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = - (this map p).fold(false.B)(_ || _) - - /** Outputs true if the vector contains at least one element equal to x (using - * the === operator). - */ - def contains(x: T)(implicit ev: T <:< UInt): Bool = macro VecTransform.contains - - /** @group SourceInfoTransformMacro */ - def do_contains(x: T)(implicit sourceInfo: SourceInfo, ev: T <:< UInt, compileOptions: CompileOptions): Bool = - this.exists(_ === x) - - /** Outputs the number of elements for which p is true. - */ - def count(p: T => Bool): UInt = macro SourceInfoTransform.pArg - - /** @group SourceInfoTransformMacro */ - def do_count(p: T => Bool)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = - SeqUtils.count(this map p) - - /** Helper function that appends an index (literal value) to each element, - * useful for hardware generators which output an index. - */ - private def indexWhereHelper(p: T => Bool) = this map p zip (0 until length).map(i => i.asUInt) - - /** Outputs the index of the first element for which p outputs true. - */ - def indexWhere(p: T => Bool): UInt = macro SourceInfoTransform.pArg - - /** @group SourceInfoTransformMacro */ - def do_indexWhere(p: T => Bool)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = - SeqUtils.priorityMux(indexWhereHelper(p)) - - /** Outputs the index of the last element for which p outputs true. - */ - def lastIndexWhere(p: T => Bool): UInt = macro SourceInfoTransform.pArg - - /** @group SourceInfoTransformMacro */ - def do_lastIndexWhere(p: T => Bool)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = - SeqUtils.priorityMux(indexWhereHelper(p).reverse) - - /** Outputs the index of the element for which p outputs true, assuming that - * the there is exactly one such element. - * - * The implementation may be more efficient than a priority mux, but - * incorrect results are possible if there is not exactly one true element. - * - * @note the assumption that there is only one element for which p outputs - * true is NOT checked (useful in cases where the condition doesn't always - * hold, but the results are not used in those cases) - */ - def onlyIndexWhere(p: T => Bool): UInt = macro SourceInfoTransform.pArg - - /** @group SourceInfoTransformMacro */ - def do_onlyIndexWhere(p: T => Bool)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = - SeqUtils.oneHotMux(indexWhereHelper(p)) -} - -/** Base class for Aggregates based on key values pairs of String and Data - * - * Record should only be extended by libraries and fairly sophisticated generators. - * RTL writers should use [[Bundle]]. See [[Record#elements]] for an example. - */ -abstract class Record(private[chisel3] implicit val compileOptions: CompileOptions) extends Aggregate { - private[chisel3] override def bind(target: Binding, parentDirection: SpecifiedDirection): Unit = { - try { - super.bind(target, parentDirection) - } catch { // nasty compatibility mode shim, where anything flies - case e: MixedDirectionAggregateException if !compileOptions.dontAssumeDirectionality => - val resolvedDirection = SpecifiedDirection.fromParent(parentDirection, specifiedDirection) - direction = resolvedDirection match { - case SpecifiedDirection.Unspecified => ActualDirection.Bidirectional(ActualDirection.Default) - case SpecifiedDirection.Flip => ActualDirection.Bidirectional(ActualDirection.Flipped) - case _ => ActualDirection.Bidirectional(ActualDirection.Default) - } - } - } - - /** Creates a Bundle literal of this type with specified values. this must be a chisel type. - * - * @param elems literal values, specified as a pair of the Bundle field to the literal value. - * The Bundle field is specified as a function from an object of this type to the field. - * Fields that aren't initialized to DontCare, and assignment to a wire will overwrite any - * existing value with DontCare. - * @return a Bundle literal of this type with subelement values specified - * - * @example {{{ - * class MyBundle extends Bundle { - * val a = UInt(8.W) - * val b = Bool() - * } - * - * (mew MyBundle).Lit( - * _.a -> 42.U, - * _.b -> true.B - * ) - * }}} - */ - private[chisel3] def _makeLit(elems: (this.type => (Data, Data))*): this.type = { // scalastyle:ignore line.size.limit method.length method.name cyclomatic.complexity - // Returns pairs of all fields, element-level and containers, in a Record and their path names - def getRecursiveFields(data: Data, path: String): Seq[(Data, String)] = data match { - case data: Record => data.elements.map { case (fieldName, fieldData) => - getRecursiveFields(fieldData, s"$path.$fieldName") - }.fold(Seq(data -> path)) { _ ++ _ } - case data => Seq(data -> path) // we don't support or recurse into other Aggregate types here - } - - // Returns pairs of corresponding fields between two Records of the same type - def getMatchedFields(x: Data, y: Data): Seq[(Data, Data)] = (x, y) match { - case (x: Element, y: Element) => - require(x typeEquivalent y) - Seq(x -> y) - case (x: Record, y: Record) => - (x.elements zip y.elements).map { case ((xName, xElt), (yName, yElt)) => - require(xName == yName) // assume fields returned in same, deterministic order - getMatchedFields(xElt, yElt) - }.fold(Seq(x -> y)) { _ ++ _ } - } - - requireIsChiselType(this, "bundle literal constructor model") - val clone = cloneType - val cloneFields = getRecursiveFields(clone, "(bundle root)").toMap - - // Create the Bundle literal binding from litargs of arguments - val bundleLitMap = elems.map { fn => fn(clone) }.flatMap { case (field, value) => - val fieldName = cloneFields.getOrElse(field, - throw new BundleLiteralException(s"field $field (with value $value) is not a field," + - s" ensure the field is specified as a function returning a field on an object of class ${this.getClass}," + - s" eg '_.a' to select hypothetical bundle field 'a'") - ) - val valueBinding = value.topBindingOpt match { - case Some(litBinding: LitBinding) => litBinding - case _ => throw new BundleLiteralException(s"field $fieldName specified with non-literal value $value") - } - - field match { // Get the litArg(s) for this field - case field: Bits => - if (field.getClass != value.getClass) { // TODO typeEquivalent is too strict because it checks width - throw new BundleLiteralException(s"Field $fieldName $field specified with non-type-equivalent value $value") - } - val litArg = valueBinding match { - case ElementLitBinding(litArg) => litArg - case BundleLitBinding(litMap) => litMap.getOrElse(value, - throw new BundleLiteralException(s"Field $fieldName specified with unspecified value")) - } - Seq(field -> litArg) - case field: Record => - if (!(field typeEquivalent value)) { - throw new BundleLiteralException(s"field $fieldName $field specified with non-type-equivalent value $value") - } - // Copy the source BundleLitBinding with fields (keys) remapped to the clone - val remap = getMatchedFields(value, field).toMap - value.topBinding.asInstanceOf[BundleLitBinding].litMap.map { case (valueField, valueValue) => - remap(valueField) -> valueValue - } - case field: EnumType => { - if (!(field typeEquivalent value)) { - throw new BundleLiteralException(s"field $fieldName $field specified with non-type-equivalent enum value $value") - } - val litArg = valueBinding match { - case ElementLitBinding(litArg) => litArg - } - Seq(field -> litArg) - } - case _ => throw new BundleLiteralException(s"unsupported field $fieldName of type $field") - } - } // don't convert to a Map yet to preserve duplicate keys - val duplicates = bundleLitMap.map(_._1).groupBy(identity).collect { case (x, elts) if elts.size > 1 => x } - if (!duplicates.isEmpty) { - val duplicateNames = duplicates.map(cloneFields(_)).mkString(", ") - throw new BundleLiteralException(s"duplicate fields $duplicateNames in Bundle literal constructor") - } - clone.bind(BundleLitBinding(bundleLitMap.toMap)) - clone - } - - /** The collection of [[Data]] - * - * This underlying datastructure is a ListMap because the elements must - * remain ordered for serialization/deserialization. Elements added later - * are higher order when serialized (this is similar to [[Vec]]). For example: - * {{{ - * // Assume we have some type MyRecord that creates a Record from the ListMap - * val record = MyRecord(ListMap("fizz" -> UInt(16.W), "buzz" -> UInt(16.W))) - * // "buzz" is higher order because it was added later than "fizz" - * record("fizz") := "hdead".U - * record("buzz") := "hbeef".U - * val uint = record.asUInt - * assert(uint === "hbeefdead".U) // This will pass - * }}} - */ - override def toString: String = { - val bindingString = topBindingOpt match { - case Some(BundleLitBinding(_)) => - val contents = elements.toList.reverse.map { case (name, data) => - s"$name=$data" - }.mkString(", ") - s"($contents)" - case _ => bindingToString - } - s"$className$bindingString" - } - - val elements: ListMap[String, Data] - - /** Name for Pretty Printing */ - def className: String = this.getClass.getSimpleName - - private[chisel3] override def typeEquivalent(that: Data): Boolean = that match { - case that: Record => - this.getClass == that.getClass && - this.elements.size == that.elements.size && - this.elements.forall{case (name, model) => - that.elements.contains(name) && - (that.elements(name) typeEquivalent model)} - case _ => false - } - - // NOTE: This sets up dependent references, it can be done before closing the Module - private[chisel3] override def _onModuleClose: Unit = { // scalastyle:ignore method.name - // Since Bundle names this via reflection, it is impossible for two elements to have the same - // identifier; however, Namespace sanitizes identifiers to make them legal for Firrtl/Verilog - // which can cause collisions - val _namespace = Namespace.empty - for ((name, elt) <- elements) { elt.setRef(this, _namespace.name(name, leadingDigitOk=true)) } - } - - private[chisel3] final def allElements: Seq[Element] = elements.toIndexedSeq.flatMap(_._2.allElements) - - override def getElements: Seq[Data] = elements.toIndexedSeq.map(_._2) - - // Helper because Bundle elements are reversed before printing - private[chisel3] def toPrintableHelper(elts: Seq[(String, Data)]): Printable = { - // scalastyle:off if.brace - val xs = - if (elts.isEmpty) List.empty[Printable] // special case because of dropRight below - else elts flatMap { case (name, data) => - List(PString(s"$name -> "), data.toPrintable, PString(", ")) - } dropRight 1 // Remove trailing ", " - // scalastyle:on if.brace - PString(s"$className(") + Printables(xs) + PString(")") - } - /** Default "pretty-print" implementation - * Analogous to printing a Map - * Results in "`\$className(elt0.name -> elt0.value, ...)`" - */ - def toPrintable: Printable = toPrintableHelper(elements.toList) -} - -/** - * Mix-in for Bundles that have arbitrary Seqs of Chisel types that aren't - * involved in hardware construction. - * - * Used to avoid raising an error/exception when a Seq is a public member of the - * bundle. - * This is useful if we those public Seq fields in the Bundle are unrelated to - * hardware construction. - */ -trait IgnoreSeqInBundle { - this: Bundle => - - override def ignoreSeq: Boolean = true -} - -class AutoClonetypeException(message: String) extends ChiselException(message) - -package experimental { - - class BundleLiteralException(message: String) extends ChiselException(message) - -} - -/** Base class for data types defined as a bundle of other data types. - * - * Usage: extend this class (either as an anonymous or named class) and define - * members variables of [[Data]] subtypes to be elements in the Bundle. - * - * Example of an anonymous IO bundle - * {{{ - * class MyModule extends Module { - * val io = IO(new Bundle { - * val in = Input(UInt(64.W)) - * val out = Output(SInt(128.W)) - * }) - * } - * }}} - * - * Or as a named class - * {{{ - * class Packet extends Bundle { - * val header = UInt(16.W) - * val addr = UInt(16.W) - * val data = UInt(32.W) - * } - * class MyModule extends Module { - * val io = IO(new Bundle { - * val inPacket = Input(new Packet) - * val outPacket = Output(new Packet) - * }) - * val reg = Reg(new Packet) - * reg <> io.inPacket - * io.outPacket <> reg - * } - * }}} - */ -abstract class Bundle(implicit compileOptions: CompileOptions) extends Record { - override def className: String = this.getClass.getSimpleName match { - case name if name.startsWith("$anon$") => "AnonymousBundle" // fallback for anonymous Bundle case - case "" => "AnonymousBundle" // ditto, but on other platforms - case name => name - } - /** The collection of [[Data]] - * - * Elements defined earlier in the Bundle are higher order upon - * serialization. For example: - * @example - * {{{ - * class MyBundle extends Bundle { - * val foo = UInt(16.W) - * val bar = UInt(16.W) - * } - * // Note that foo is higher order because its defined earlier in the Bundle - * val bundle = Wire(new MyBundle) - * bundle.foo := 0x1234.U - * bundle.bar := 0x5678.U - * val uint = bundle.asUInt - * assert(uint === "h12345678".U) // This will pass - * }}} - */ - final lazy val elements: ListMap[String, Data] = { - val nameMap = LinkedHashMap[String, Data]() - for (m <- getPublicFields(classOf[Bundle])) { - getBundleField(m) match { - case Some(d: Data) => - if (nameMap contains m.getName) { - require(nameMap(m.getName) eq d) - } else { - nameMap(m.getName) = d - } - case None => - if (!ignoreSeq) { - m.invoke(this) match { - case s: scala.collection.Seq[Any] if s.nonEmpty => s.head match { - // Ignore empty Seq() - case d: Data => throwException("Public Seq members cannot be used to define Bundle elements " + - s"(found public Seq member '${m.getName}'). " + - "Either use a Vec if all elements are of the same type, or MixedVec if the elements " + - "are of different types. If this Seq member is not intended to construct RTL, mix in the trait " + - "IgnoreSeqInBundle.") - case _ => // don't care about non-Data Seq - } - case _ => // not a Seq - } - } - } - } - ListMap(nameMap.toSeq sortWith { case ((an, a), (bn, b)) => (a._id > b._id) || ((a eq b) && (an > bn)) }: _*) - // scalastyle:ignore method.length - } - - /** - * Overridden by [[IgnoreSeqInBundle]] to allow arbitrary Seqs of Chisel elements. - */ - def ignoreSeq: Boolean = false - - /** Returns a field's contained user-defined Bundle element if it appears to - * be one, otherwise returns None. - */ - private def getBundleField(m: java.lang.reflect.Method): Option[Data] = m.invoke(this) match { - case d: Data => Some(d) - case Some(d: Data) => Some(d) - case _ => None - } - - // Memoize the outer instance for autoclonetype, especially where this is context-dependent - // (like the outer module or enclosing Bundles). - private var _outerInst: Option[Object] = None - - // For autoclonetype, record possible candidates for outer instance. - // _outerInst should always take precedence, since it should be propagated from the original - // object which has the most accurate context. - private val _containingModule: Option[BaseModule] = Builder.currentModule - private val _containingBundles: Seq[Bundle] = Builder.updateBundleStack(this) - - override def cloneType : this.type = { // scalastyle:ignore cyclomatic.complexity method.length - // This attempts to infer constructor and arguments to clone this Bundle subtype without - // requiring the user explicitly overriding cloneType. - import scala.language.existentials - import scala.reflect.runtime.universe._ - - val clazz = this.getClass - - def autoClonetypeError(desc: String): Nothing = { - throw new AutoClonetypeException(s"Unable to automatically infer cloneType on $clazz: $desc") - } - - def validateClone(clone: Bundle, equivDiagnostic: String): Unit = { - if (!clone.typeEquivalent(this)) { - autoClonetypeError(s"Automatically cloned $clone not type-equivalent to base $this. " + equivDiagnostic) - } - - for ((name, field) <- elements) { - if (clone.elements(name) eq field) { - autoClonetypeError(s"Automatically cloned $clone has field $name aliased with base $this." + - " In the future, this can be solved by wrapping the field in Field(...)," + - " see https://github.com/freechipsproject/chisel3/pull/909." + - " For now, ensure Chisel types used in the Bundle definition are passed through constructor arguments," + - " or wrapped in Input(...), Output(...), or Flipped(...) if appropriate.") - } - } - } - - val mirror = runtimeMirror(clazz.getClassLoader) - val classSymbolOption = try { - Some(mirror.reflect(this).symbol) - } catch { - case e: scala.reflect.internal.Symbols#CyclicReference => None // Workaround for a scala bug - } - - val enclosingClassOption = (clazz.getEnclosingClass, classSymbolOption) match { - case (null, _) => None - case (_, Some(classSymbol)) if classSymbol.isStatic => None // allows support for members of companion objects - case (outerClass, _) => Some(outerClass) - } - - // For compatibility with pre-3.1, where null is tried as an argument to the constructor. - // This stores potential error messages which may be used later. - var outerClassError: Option[String] = None - - // Check if this is an inner class, and if so, try to get the outer instance - val outerClassInstance = enclosingClassOption.map { outerClass => - def canAssignOuterClass(x: Object) = outerClass.isAssignableFrom(x.getClass) - - val outerInstance = _outerInst match { - case Some(outerInstance) => outerInstance // use _outerInst if defined - case None => // determine outer instance if not already recorded - try { - // Prefer this if it works, but doesn't work in all cases, namely anonymous inner Bundles - val outer = clazz.getDeclaredField("$outer").get(this) - _outerInst = Some(outer) - outer - } catch { - case (_: NoSuchFieldException | _: IllegalAccessException) => - // Fallback using guesses based on common patterns - val allOuterCandidates = Seq( - _containingModule.toSeq, - _containingBundles - ).flatten.distinct - allOuterCandidates.filter(canAssignOuterClass(_)) match { - case outer :: Nil => - _outerInst = Some(outer) // record the guess for future use - outer - case Nil => // TODO: replace with fatal autoClonetypeError once compatibility period is dropped - outerClassError = Some(s"Unable to determine instance of outer class $outerClass," + - s" no candidates assignable to outer class types; examined $allOuterCandidates") - null - case candidates => // TODO: replace with fatal autoClonetypeError once compatibility period is dropped - outerClassError = Some(s"Unable to determine instance of outer class $outerClass," + - s" multiple possible candidates $candidates assignable to outer class type") - null - } - } - } - (outerClass, outerInstance) - } - - // If possible (constructor with no arguments), try Java reflection first - // This handles two cases that Scala reflection doesn't: - // 1. getting the ClassSymbol of a class with an anonymous outer class fails with a - // CyclicReference exception - // 2. invoking the constructor of an anonymous inner class seems broken (it expects the outer - // class as an argument, but fails because the number of arguments passed in is incorrect) - if (clazz.getConstructors.size == 1) { - var ctor = clazz.getConstructors.head - val argTypes = ctor.getParameterTypes.toList - val clone = (argTypes, outerClassInstance) match { - case (Nil, None) => // no arguments, no outer class, invoke constructor directly - Some(ctor.newInstance().asInstanceOf[this.type]) - case (argType :: Nil, Some((_, outerInstance))) => - if (outerInstance == null) { - Builder.deprecated(s"chisel3.1 autoclonetype failed, falling back to 3.0 behavior using null as the outer instance." + // scalastyle:ignore line.size.limit - s" Autoclonetype failure reason: ${outerClassError.get}", - Some(s"$clazz")) - Some(ctor.newInstance(outerInstance).asInstanceOf[this.type]) - } else if (argType isAssignableFrom outerInstance.getClass) { - Some(ctor.newInstance(outerInstance).asInstanceOf[this.type]) - } else { - None - } - case _ => None - - } - clone match { - case Some(clone) => - clone._outerInst = this._outerInst - validateClone(clone, "Constructor argument values were not inferred, ensure constructor is deterministic.") - return clone.asInstanceOf[this.type] - case None => - } - } - - // Get constructor parameters and accessible fields - val classSymbol = classSymbolOption.getOrElse(autoClonetypeError(s"scala reflection failed." + - " This is known to occur with inner classes on anonymous outer classes." + - " In those cases, autoclonetype only works with no-argument constructors, or you can define a custom cloneType.")) // scalastyle:ignore line.size.limit - - val decls = classSymbol.typeSignature.decls - val ctors = decls.collect { case meth: MethodSymbol if meth.isConstructor => meth } - if (ctors.size != 1) { - autoClonetypeError(s"found multiple constructors ($ctors)." + - " Either remove all but the default constructor, or define a custom cloneType method.") - } - val ctor = ctors.head - val ctorParamss = ctor.paramLists - val ctorParams = ctorParamss match { - case Nil => List() - case ctorParams :: Nil => ctorParams - case ctorParams :: ctorImplicits :: Nil => ctorParams ++ ctorImplicits - case _ => autoClonetypeError(s"internal error, unexpected ctorParamss = $ctorParamss") - } - val ctorParamsNames = ctorParams.map(_.name.toString) - - // Special case for anonymous inner classes: their constructor consists of just the outer class reference - // Scala reflection on anonymous inner class constructors seems broken - if (ctorParams.size == 1 && outerClassInstance.isDefined && - ctorParams.head.typeSignature == mirror.classSymbol(outerClassInstance.get._1).toType) { - // Fall back onto Java reflection - val ctors = clazz.getConstructors - require(ctors.size == 1) // should be consistent with Scala constructors - try { - val clone = ctors.head.newInstance(outerClassInstance.get._2).asInstanceOf[this.type] - clone._outerInst = this._outerInst - - validateClone(clone, "Outer class instance was inferred, ensure constructor is deterministic.") - return clone - } catch { - case e @ (_: java.lang.reflect.InvocationTargetException | _: IllegalArgumentException) => - autoClonetypeError(s"unexpected failure at constructor invocation, got $e.") - } - } - - // Get all the class symbols up to (but not including) Bundle and get all the accessors. - // (each ClassSymbol's decls only includes those declared in the class itself) - val bundleClassSymbol = mirror.classSymbol(classOf[Bundle]) - val superClassSymbols = classSymbol.baseClasses.takeWhile(_ != bundleClassSymbol) - val superClassDecls = superClassSymbols.map(_.typeSignature.decls).flatten - val accessors = superClassDecls.collect { case meth: MethodSymbol if meth.isParamAccessor => meth } - - // Get constructor argument values - // Check that all ctor params are immutable and accessible. Immutability is required to avoid - // potential subtle bugs (like values changing after cloning). - // This also generates better error messages (all missing elements shown at once) instead of - // failing at the use site one at a time. - val accessorsName = accessors.filter(_.isStable).map(_.name.toString) - val paramsDiff = ctorParamsNames.toSet -- accessorsName.toSet - if (!paramsDiff.isEmpty) { - // scalastyle:off line.size.limit - autoClonetypeError(s"constructor has parameters (${paramsDiff.toList.sorted.mkString(", ")}) that are not both immutable and accessible." + - " Either make all parameters immutable and accessible (vals) so cloneType can be inferred, or define a custom cloneType method.") - // scalastyle:on line.size.limit - } - - // Get all the argument values - val accessorsMap = accessors.map(accessor => accessor.name.toString -> accessor).toMap - val instanceReflect = mirror.reflect(this) - val ctorParamsNameVals = ctorParamsNames.map { - paramName => paramName -> instanceReflect.reflectMethod(accessorsMap(paramName)).apply() - } - - // Opportunistic sanity check: ensure any arguments of type Data is not bound - // (which could lead to data conflicts, since it's likely the user didn't know to re-bind them). - // This is not guaranteed to catch all cases (for example, Data in Tuples or Iterables). - val boundDataParamNames = ctorParamsNameVals.collect { - case (paramName, paramVal: Data) if paramVal.topBindingOpt.isDefined => paramName - } - if (boundDataParamNames.nonEmpty) { - // scalastyle:off line.size.limit - autoClonetypeError(s"constructor parameters (${boundDataParamNames.sorted.mkString(", ")}) have values that are hardware types, which is likely to cause subtle errors." + - " Use chisel types instead: use the value before it is turned to a hardware type (with Wire(...), Reg(...), etc) or use chiselTypeOf(...) to extract the chisel type.") - // scalastyle:on line.size.limit - } - - // Clone unbound parameters in case they are being used as bundle fields. - val ctorParamsVals = ctorParamsNameVals.map { - case (_, paramVal: Data) => paramVal.cloneTypeFull - case (_, paramVal) => paramVal - } - - // Invoke ctor - val classMirror = outerClassInstance match { - case Some((_, null)) => autoClonetypeError(outerClassError.get) // deals with the null hack for 3.0 compatibility - case Some((_, outerInstance)) => mirror.reflect(outerInstance).reflectClass(classSymbol) - case _ => mirror.reflectClass(classSymbol) - } - val clone = classMirror.reflectConstructor(ctor).apply(ctorParamsVals:_*).asInstanceOf[this.type] - clone._outerInst = this._outerInst - - validateClone(clone, - "Constructor argument values were inferred:" + - " ensure that variable names are consistent and have the same value throughout the constructor chain," + - " and that the constructor is deterministic." - ) - clone - } - - /** Default "pretty-print" implementation - * Analogous to printing a Map - * Results in "`Bundle(elt0.name -> elt0.value, ...)`" - * @note The order is reversed from the order of elements in order to print - * the fields in the order they were defined - */ - override def toPrintable: Printable = toPrintableHelper(elements.toList.reverse) - // scalastyle:off method.length -} -// scalastyle:off file.size.limit diff --git a/chiselFrontend/src/main/scala/chisel3/Annotation.scala b/chiselFrontend/src/main/scala/chisel3/Annotation.scala deleted file mode 100644 index e54b1bf9..00000000 --- a/chiselFrontend/src/main/scala/chisel3/Annotation.scala +++ /dev/null @@ -1,94 +0,0 @@ -// See LICENSE for license details. - -package chisel3.experimental - -import scala.language.existentials -import chisel3.internal.{Builder, InstanceId, LegacyModule} -import chisel3.{CompileOptions, Data} -import firrtl.Transform -import firrtl.annotations._ -import firrtl.options.Unserializable -import firrtl.transforms.{DontTouchAnnotation, NoDedupAnnotation} - -/** Interface for Annotations in Chisel - * - * Defines a conversion to a corresponding FIRRTL Annotation - */ -trait ChiselAnnotation { - /** Conversion to FIRRTL Annotation */ - def toFirrtl: Annotation -} - -/** Mixin for [[ChiselAnnotation]] that instantiates an associated FIRRTL Transform when this Annotation is present - * during a run of - * [[Driver$.execute(args:Array[String],dut:()=>chisel3\.RawModule)* Driver.execute]]. - * Automatic Transform instantiation is *not* supported when the Circuit and Annotations are serialized before invoking - * FIRRTL. - */ -trait RunFirrtlTransform extends ChiselAnnotation { - def transformClass: Class[_ <: Transform] -} - - -// This exists for implementation reasons, we don't want people using this type directly -final case class ChiselLegacyAnnotation private[chisel3] ( - component: InstanceId, - transformClass: Class[_ <: Transform], - value: String) extends ChiselAnnotation with RunFirrtlTransform { - def toFirrtl: Annotation = Annotation(component.toNamed, transformClass, value) -} -private[chisel3] object ChiselLegacyAnnotation - -object annotate { // scalastyle:ignore object.name - def apply(anno: ChiselAnnotation): Unit = { - Builder.annotations += anno - } -} - -/** Marks that a module to be ignored in Dedup Transform in Firrtl pass - * - * @example {{{ - * def fullAdder(a: UInt, b: UInt, myName: String): UInt = { - * val m = Module(new Module { - * val io = IO(new Bundle { - * val a = Input(UInt(32.W)) - * val b = Input(UInt(32.W)) - * val out = Output(UInt(32.W)) - * }) - * override def desiredName = "adder_" + myNname - * io.out := io.a + io.b - * }) - * doNotDedup(m) - * m.io.a := a - * m.io.b := b - * m.io.out - * } - * - *class AdderTester extends Module - * with ConstantPropagationTest { - * val io = IO(new Bundle { - * val a = Input(UInt(32.W)) - * val b = Input(UInt(32.W)) - * val out = Output(Vec(2, UInt(32.W))) - * }) - * - * io.out(0) := fullAdder(io.a, io.b, "mod1") - * io.out(1) := fullAdder(io.a, io.b, "mod2") - * } - * }}} - * - * @note Calling this on [[Data]] creates an annotation that Chisel emits to a separate annotations - * file. This file must be passed to FIRRTL independently of the `.fir` file. The execute methods - * in [[chisel3.Driver]] will pass the annotations to FIRRTL automatically. - */ - -object doNotDedup { // scalastyle:ignore object.name - /** Marks a module to be ignored in Dedup Transform in Firrtl - * - * @param module The module to be marked - * @return Unmodified signal `module` - */ - def apply[T <: LegacyModule](module: T)(implicit compileOptions: CompileOptions): Unit = { - annotate(new ChiselAnnotation { def toFirrtl = NoDedupAnnotation(module.toNamed) }) - } -} diff --git a/chiselFrontend/src/main/scala/chisel3/Assert.scala b/chiselFrontend/src/main/scala/chisel3/Assert.scala deleted file mode 100644 index af58bce6..00000000 --- a/chiselFrontend/src/main/scala/chisel3/Assert.scala +++ /dev/null @@ -1,92 +0,0 @@ -// See LICENSE for license details. - -package chisel3 - -import scala.reflect.macros.blackbox.Context -import scala.language.experimental.macros - -import chisel3.internal._ -import chisel3.internal.Builder.pushCommand -import chisel3.internal.firrtl._ -import chisel3.internal.sourceinfo.SourceInfo - -object assert { // scalastyle:ignore object.name - /** Checks for a condition to be valid in the circuit at all times. If the - * condition evaluates to false, the circuit simulation stops with an error. - * - * Does not fire when in reset (defined as the encapsulating Module's - * reset). If your definition of reset is not the encapsulating Module's - * reset, you will need to gate this externally. - * - * May be called outside of a Module (like defined in a function), so - * functions using assert make the standard Module assumptions (single clock - * and single reset). - * - * @param cond condition, assertion fires (simulation fails) when false - * @param message optional format string to print when the assertion fires - * @param data optional bits to print in the message formatting - * - * @note See [[printf.apply(fmt:String* printf]] for format string documentation - * @note currently cannot be used in core Chisel / libraries because macro - * defs need to be compiled first and the SBT project is not set up to do - * that - */ - // Macros currently can't take default arguments, so we need two functions to emulate defaults. - def apply(cond: Bool, message: String, data: Bits*)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Unit = macro apply_impl_msg_data // scalastyle:ignore line.size.limit - def apply(cond: Bool)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Unit = macro apply_impl - - def apply_impl_msg_data(c: Context)(cond: c.Tree, message: c.Tree, data: c.Tree*)(sourceInfo: c.Tree, compileOptions: c.Tree): c.Tree = { // scalastyle:ignore line.size.limit - import c.universe._ - val p = c.enclosingPosition - val condStr = s"${p.source.file.name}:${p.line} ${p.lineContent.trim}" - val apply_impl_do = symbolOf[this.type].asClass.module.info.member(TermName("apply_impl_do")) - q"$apply_impl_do($cond, $condStr, _root_.scala.Some($message), ..$data)($sourceInfo, $compileOptions)" - } - - def apply_impl(c: Context)(cond: c.Tree)(sourceInfo: c.Tree, compileOptions: c.Tree): c.Tree = { - import c.universe._ - val p = c.enclosingPosition - val condStr = s"${p.source.file.name}:${p.line} ${p.lineContent.trim}" - val apply_impl_do = symbolOf[this.type].asClass.module.info.member(TermName("apply_impl_do")) - q"$apply_impl_do($cond, $condStr, _root_.scala.None)($sourceInfo, $compileOptions)" - } - - def apply_impl_do(cond: Bool, line: String, message: Option[String], data: Bits*)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions) { // scalastyle:ignore line.size.limit - val escLine = line.replaceAll("%", "%%") - when (!(cond || Module.reset.asBool)) { - val fmt = message match { - case Some(msg) => - s"Assertion failed: $msg\n at $escLine\n" - case None => s"Assertion failed\n at $escLine\n" - } - printf.printfWithoutReset(fmt, data:_*) - pushCommand(Stop(sourceInfo, Builder.forcedClock.ref, 1)) - } - } - - /** An elaboration-time assertion, otherwise the same as the above run-time - * assertion. */ - def apply(cond: Boolean, message: => String) { - Predef.assert(cond, message) - } - - /** A workaround for default-value overloading problems in Scala, just - * 'assert(cond, "")' */ - def apply(cond: Boolean) { - Predef.assert(cond, "") - } -} - -object stop { // scalastyle:ignore object.name - /** Terminate execution with a failure code. */ - def apply(code: Int)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Unit = { - when (!Module.reset.asBool) { - pushCommand(Stop(sourceInfo, Builder.forcedClock.ref, code)) - } - } - - /** Terminate execution, indicating success. */ - def apply()(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Unit = { - stop(0) - } -} diff --git a/chiselFrontend/src/main/scala/chisel3/Attach.scala b/chiselFrontend/src/main/scala/chisel3/Attach.scala deleted file mode 100644 index 25c83d9a..00000000 --- a/chiselFrontend/src/main/scala/chisel3/Attach.scala +++ /dev/null @@ -1,46 +0,0 @@ -// See LICENSE for license details. - -package chisel3.experimental - -import chisel3.RawModule -import chisel3.internal._ -import chisel3.internal.Builder.pushCommand -import chisel3.internal.firrtl._ -import chisel3.internal.sourceinfo.SourceInfo - -object attach { // scalastyle:ignore object.name - // Exceptions that can be generated by attach - case class AttachException(message: String) extends ChiselException(message) - def ConditionalAttachException: AttachException = // scalastyle:ignore method.name - AttachException(": Conditional attach is not allowed!") - - // Actual implementation - private[chisel3] def impl(elts: Seq[Analog], contextModule: RawModule)(implicit sourceInfo: SourceInfo): Unit = { - if (Builder.whenDepth != 0) throw ConditionalAttachException - - // TODO Check that references are valid and can be attached - - pushCommand(Attach(sourceInfo, elts.map(_.lref))) - } - - /** Create an electrical connection between [[Analog]] components - * - * @param elts The components to attach - * - * @example - * {{{ - * val a1 = Wire(Analog(32.W)) - * val a2 = Wire(Analog(32.W)) - * attach(a1, a2) - * }}} - */ - def apply(elts: Analog*)(implicit sourceInfo: SourceInfo): Unit = { - try { - impl(elts, Builder.forcedUserModule) - } catch { - case AttachException(message) => - throwException(elts.mkString("Attaching (", ", ", s") failed @$message")) - } - } -} - diff --git a/chiselFrontend/src/main/scala/chisel3/Bits.scala b/chiselFrontend/src/main/scala/chisel3/Bits.scala deleted file mode 100644 index 43c34d9d..00000000 --- a/chiselFrontend/src/main/scala/chisel3/Bits.scala +++ /dev/null @@ -1,2289 +0,0 @@ -// See LICENSE for license details. - -package chisel3 - -import scala.language.experimental.macros - -import chisel3.experimental.{FixedPoint, Interval} -import chisel3.internal._ -import chisel3.internal.Builder.pushOp -import chisel3.internal.firrtl._ -import chisel3.internal.sourceinfo.{SourceInfo, SourceInfoTransform, SourceInfoWhiteboxTransform, - UIntTransform} -import chisel3.internal.firrtl.PrimOp._ -import _root_.firrtl.{ir => firrtlir} -import _root_.firrtl.{constraint => firrtlconstraint} - -// scalastyle:off method.name line.size.limit file.size.limit - -/** Exists to unify common interfaces of [[Bits]] and [[Reset]]. - * - * @note This is a workaround because macros cannot override abstract methods. - */ -private[chisel3] sealed trait ToBoolable extends Element { - - /** Casts this $coll to a [[Bool]] - * - * @note The width must be known and equal to 1 - */ - final def asBool(): Bool = macro SourceInfoWhiteboxTransform.noArg - - /** @group SourceInfoTransformMacro */ - def do_asBool(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool - - /** Casts this $coll to a [[Bool]] - * - * @note The width must be known and equal to 1 - */ - final def toBool(): Bool = macro SourceInfoWhiteboxTransform.noArg - - /** @group SourceInfoTransformMacro */ - def do_toBool(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool -} - -/** A data type for values represented by a single bitvector. This provides basic bitwise operations. - * - * @groupdesc Bitwise Bitwise hardware operators - * @define coll [[Bits]] - * @define sumWidthInt @note The width of the returned $coll is `width of this` + `that`. - * @define sumWidth @note The width of the returned $coll is `width of this` + `width of that`. - * @define unchangedWidth @note The width of the returned $coll is unchanged, i.e., the `width of this`. - */ -sealed abstract class Bits(private[chisel3] val width: Width) extends Element with ToBoolable { //scalastyle:off number.of.methods - // TODO: perhaps make this concrete? - // Arguments for: self-checking code (can't do arithmetic on bits) - // Arguments against: generates down to a FIRRTL UInt anyways - - // Only used for in a few cases, hopefully to be removed - private[chisel3] def cloneTypeWidth(width: Width): this.type - - def cloneType: this.type = cloneTypeWidth(width) - - /** Tail operator - * - * @param n the number of bits to remove - * @return This $coll with the `n` most significant bits removed. - * @group Bitwise - */ - final def tail(n: Int): UInt = macro SourceInfoTransform.nArg - - /** Head operator - * - * @param n the number of bits to take - * @return The `n` most significant bits of this $coll - * @group Bitwise - */ - final def head(n: Int): UInt = macro SourceInfoTransform.nArg - - /** @group SourceInfoTransformMacro */ - def do_tail(n: Int)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = { - val w = width match { - case KnownWidth(x) => - require(x >= n, s"Can't tail($n) for width $x < $n") - Width(x - n) - case UnknownWidth() => Width() - } - binop(sourceInfo, UInt(width = w), TailOp, n) - } - - /** @group SourceInfoTransformMacro */ - def do_head(n: Int)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = { - width match { - case KnownWidth(x) => require(x >= n, s"Can't head($n) for width $x < $n") - case UnknownWidth() => - } - binop(sourceInfo, UInt(Width(n)), HeadOp, n) - } - - /** Returns the specified bit on this $coll as a [[Bool]], statically addressed. - * - * @param x an index - * @return the specified bit - */ - final def apply(x: BigInt): Bool = macro SourceInfoTransform.xArg - - /** @group SourceInfoTransformMacro */ - final def do_apply(x: BigInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = { - if (x < 0) { - Builder.error(s"Negative bit indices are illegal (got $x)") - } - // This preserves old behavior while a more more consistent API is under debate - // See https://github.com/freechipsproject/chisel3/issues/867 - litOption.map { value => - (((value >> castToInt(x, "Index")) & 1) == 1).asBool - }.getOrElse { - requireIsHardware(this, "bits to be indexed") - pushOp(DefPrim(sourceInfo, Bool(), BitsExtractOp, this.ref, ILit(x), ILit(x))) - } - } - - /** Returns the specified bit on this $coll as a [[Bool]], statically addressed. - * - * @param x an index - * @return the specified bit - * @note convenience method allowing direct use of [[scala.Int]] without implicits - */ - final def apply(x: Int): Bool = macro SourceInfoTransform.xArg - - /** @group SourceInfoTransformMacro */ - final def do_apply(x: Int)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = apply(BigInt(x)) - - /** Returns the specified bit on this wire as a [[Bool]], dynamically addressed. - * - * @param x a hardware component whose value will be used for dynamic addressing - * @return the specified bit - */ - final def apply(x: UInt): Bool = macro SourceInfoTransform.xArg - - /** @group SourceInfoTransformMacro */ - final def do_apply(x: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = { - val theBits = this >> x - theBits(0) - } - - /** Returns a subset of bits on this $coll from `hi` to `lo` (inclusive), statically addressed. - * - * @example - * {{{ - * myBits = 0x5 = 0b101 - * myBits(1,0) => 0b01 // extracts the two least significant bits - * }}} - * @param x the high bit - * @param y the low bit - * @return a hardware component contain the requested bits - */ - final def apply(x: Int, y: Int): UInt = macro SourceInfoTransform.xyArg - - /** @group SourceInfoTransformMacro */ - final def do_apply(x: Int, y: Int)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = { - if (x < y || y < 0) { - Builder.error(s"Invalid bit range ($x,$y)") - } - val w = x - y + 1 - // This preserves old behavior while a more more consistent API is under debate - // See https://github.com/freechipsproject/chisel3/issues/867 - litOption.map { value => - ((value >> y) & ((BigInt(1) << w) - 1)).asUInt(w.W) - }.getOrElse { - requireIsHardware(this, "bits to be sliced") - pushOp(DefPrim(sourceInfo, UInt(Width(w)), BitsExtractOp, this.ref, ILit(x), ILit(y))) - } - } - - // REVIEW TODO: again, is this necessary? Or just have this and use implicits? - /** Returns a subset of bits on this $coll from `hi` to `lo` (inclusive), statically addressed. - * - * @example - * {{{ - * myBits = 0x5 = 0b101 - * myBits(1,0) => 0b01 // extracts the two least significant bits - * }}} - * @param x the high bit - * @param y the low bit - * @return a hardware component contain the requested bits - */ - final def apply(x: BigInt, y: BigInt): UInt = macro SourceInfoTransform.xyArg - - /** @group SourceInfoTransformMacro */ - final def do_apply(x: BigInt, y: BigInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = - apply(castToInt(x, "High index"), castToInt(y, "Low index")) - - private[chisel3] def unop[T <: Data](sourceInfo: SourceInfo, dest: T, op: PrimOp): T = { - requireIsHardware(this, "bits operated on") - pushOp(DefPrim(sourceInfo, dest, op, this.ref)) - } - private[chisel3] def binop[T <: Data](sourceInfo: SourceInfo, dest: T, op: PrimOp, other: BigInt): T = { - requireIsHardware(this, "bits operated on") - pushOp(DefPrim(sourceInfo, dest, op, this.ref, ILit(other))) - } - private[chisel3] def binop[T <: Data](sourceInfo: SourceInfo, dest: T, op: PrimOp, other: Bits): T = { - requireIsHardware(this, "bits operated on") - requireIsHardware(other, "bits operated on") - pushOp(DefPrim(sourceInfo, dest, op, this.ref, other.ref)) - } - private[chisel3] def compop(sourceInfo: SourceInfo, op: PrimOp, other: Bits): Bool = { - requireIsHardware(this, "bits operated on") - requireIsHardware(other, "bits operated on") - pushOp(DefPrim(sourceInfo, Bool(), op, this.ref, other.ref)) - } - private[chisel3] def redop(sourceInfo: SourceInfo, op: PrimOp): Bool = { - requireIsHardware(this, "bits operated on") - pushOp(DefPrim(sourceInfo, Bool(), op, this.ref)) - } - - /** Pad operator - * - * @param that the width to pad to - * @return this @coll zero padded up to width `that`. If `that` is less than the width of the original component, - * this method returns the original component. - * @note For [[SInt]]s only, this will do sign extension. - * @group Bitwise - */ - final def pad(that: Int): this.type = macro SourceInfoTransform.thatArg - - /** @group SourceInfoTransformMacro */ - def do_pad(that: Int)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): this.type = this.width match { - case KnownWidth(w) if w >= that => this - case _ => binop(sourceInfo, cloneTypeWidth(this.width max Width(that)), PadOp, that) - } - - /** Bitwise inversion operator - * - * @return this $coll with each bit inverted - * @group Bitwise - */ - final def unary_~ (): Bits = macro SourceInfoWhiteboxTransform.noArg - - /** @group SourceInfoTransformMacro */ - def do_unary_~ (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bits - - /** Static left shift operator - * - * @param that an amount to shift by - * @return this $coll with `that` many zeros concatenated to its least significant end - * $sumWidthInt - * @group Bitwise - */ - // REVIEW TODO: redundant - // REVIEW TODO: should these return this.type or Bits? - final def << (that: BigInt): Bits = macro SourceInfoWhiteboxTransform.thatArg - - /** @group SourceInfoTransformMacro */ - def do_<< (that: BigInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bits - - /** Static left shift operator - * - * @param that an amount to shift by - * @return this $coll with `that` many zeros concatenated to its least significant end - * $sumWidthInt - * @group Bitwise - */ - final def << (that: Int): Bits = macro SourceInfoWhiteboxTransform.thatArg - - /** @group SourceInfoTransformMacro */ - def do_<< (that: Int)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bits - - /** Dynamic left shift operator - * - * @param that a hardware component - * @return this $coll dynamically shifted left by `that` many places, shifting in zeros from the right - * @note The width of the returned $coll is `width of this + pow(2, width of that) - 1`. - * @group Bitwise - */ - final def << (that: UInt): Bits = macro SourceInfoWhiteboxTransform.thatArg - - /** @group SourceInfoTransformMacro */ - def do_<< (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bits - - /** Static right shift operator - * - * @param that an amount to shift by - * @return this $coll with `that` many least significant bits truncated - * $unchangedWidth - * @group Bitwise - */ - // REVIEW TODO: redundant - final def >> (that: BigInt): Bits = macro SourceInfoWhiteboxTransform.thatArg - - /** @group SourceInfoTransformMacro */ - def do_>> (that: BigInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bits - - /** Static right shift operator - * - * @param that an amount to shift by - * @return this $coll with `that` many least significant bits truncated - * $unchangedWidth - * @group Bitwise - */ - final def >> (that: Int): Bits = macro SourceInfoWhiteboxTransform.thatArg - - /** @group SourceInfoTransformMacro */ - def do_>> (that: Int)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bits - - /** Dynamic right shift operator - * - * @param that a hardware component - * @return this $coll dynamically shifted right by the value of `that` component, inserting zeros into the most - * significant bits. - * $unchangedWidth - * @group Bitwise - */ - final def >> (that: UInt): Bits = macro SourceInfoWhiteboxTransform.thatArg - - /** @group SourceInfoTransformMacro */ - def do_>> (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bits - - /** Returns the contents of this wire as a [[scala.collection.Seq]] of [[Bool]]. */ - final def toBools(): Seq[Bool] = macro SourceInfoTransform.noArg - - /** @group SourceInfoTransformMacro */ - @chiselRuntimeDeprecated - @deprecated("Use asBools instead", "3.2") - def do_toBools(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Seq[Bool] = do_asBools - - /** Returns the contents of this wire as a [[scala.collection.Seq]] of [[Bool]]. */ - final def asBools(): Seq[Bool] = macro SourceInfoTransform.noArg - - /** @group SourceInfoTransformMacro */ - def do_asBools(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Seq[Bool] = - Seq.tabulate(this.getWidth)(i => this(i)) - - /** Reinterpret this $coll as an [[SInt]] - * - * @note The arithmetic value is not preserved if the most-significant bit is set. For example, a [[UInt]] of - * width 3 and value 7 (0b111) would become an [[SInt]] of width 3 and value -1. - */ - final def asSInt(): SInt = macro SourceInfoTransform.noArg - - /** @group SourceInfoTransformMacro */ - def do_asSInt(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt - - /** Reinterpret this $coll as a [[FixedPoint]]. - * - * @note The value is not guaranteed to be preserved. For example, a [[UInt]] of width 3 and value 7 (0b111) would - * become a [[FixedPoint]] with value -1. The interpretation of the number is also affected by the specified binary - * point. '''Caution is advised!''' - */ - final def asFixedPoint(that: BinaryPoint): FixedPoint = macro SourceInfoTransform.thatArg - - /** @group SourceInfoTransformMacro */ - def do_asFixedPoint(that: BinaryPoint)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = { - throwException(s"Cannot call .asFixedPoint on $this") - } - - /** Reinterpret cast as a Interval. - * - * @note value not guaranteed to be preserved: for example, an UInt of width - * 3 and value 7 (0b111) would become a FixedInt with value -1, the interpretation - * of the number is also affected by the specified binary point. Caution advised - */ - final def asInterval(that: IntervalRange): Interval = macro SourceInfoTransform.thatArg - - def do_asInterval(that: IntervalRange)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { - throwException(s"Cannot call .asInterval on $this") - } - - final def do_asBool(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = { - width match { - case KnownWidth(1) => this(0) - case _ => throwException(s"can't covert ${this.getClass.getSimpleName}$width to Bool") - } - } - - @chiselRuntimeDeprecated - @deprecated("Use asBool instead", "3.2") - final def do_toBool(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = do_asBool - - /** Concatenation operator - * - * @param that a hardware component - * @return this $coll concatenated to the most significant end of `that` - * $sumWidth - * @group Bitwise - */ - final def ## (that: Bits): UInt = macro SourceInfoTransform.thatArg - - /** @group SourceInfoTransformMacro */ - def do_## (that: Bits)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = { - val w = this.width + that.width - pushOp(DefPrim(sourceInfo, UInt(w), ConcatOp, this.ref, that.ref)) - } - - /** Default print as [[Decimal]] */ - final def toPrintable: Printable = Decimal(this) - - protected final def validateShiftAmount(x: Int): Int = { - if (x < 0) - Builder.error(s"Negative shift amounts are illegal (got $x)") - x - } -} - -/** A data type for unsigned integers, represented as a binary bitvector. Defines arithmetic operations between other - * integer types. - * - * @define coll [[UInt]] - * @define numType $coll - * @define expandingWidth @note The width of the returned $coll is `width of this` + `1`. - * @define constantWidth @note The width of the returned $coll is unchanged, i.e., `width of this`. - */ -sealed class UInt private[chisel3] (width: Width) extends Bits(width) with Num[UInt] { - override def toString: String = { - val bindingString = litOption match { - case Some(value) => s"($value)" - case _ => bindingToString - } - s"UInt$width$bindingString" - } - - private[chisel3] override def typeEquivalent(that: Data): Boolean = - that.isInstanceOf[UInt] && this.width == that.width - - private[chisel3] override def cloneTypeWidth(w: Width): this.type = - new UInt(w).asInstanceOf[this.type] - - // TODO: refactor to share documentation with Num or add independent scaladoc - /** Unary negation (expanding width) - * - * @return a $coll equal to zero minus this $coll - * $constantWidth - * @group Arithmetic - */ - final def unary_- (): UInt = macro SourceInfoTransform.noArg - - /** Unary negation (constant width) - * - * @return a $coll equal to zero minus this $coll shifted right by one. - * $constantWidth - * @group Arithmetic - */ - final def unary_-% (): UInt = macro SourceInfoTransform.noArg - - /** @group SourceInfoTransformMacro */ - def do_unary_- (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions) : UInt = 0.U - this - /** @group SourceInfoTransformMacro */ - def do_unary_-% (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = 0.U -% this - - override def do_+ (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = this +% that - override def do_- (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = this -% that - override def do_/ (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = - binop(sourceInfo, UInt(this.width), DivideOp, that) - override def do_% (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = - binop(sourceInfo, UInt(this.width), RemOp, that) - override def do_* (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = - binop(sourceInfo, UInt(this.width + that.width), TimesOp, that) - - /** Multiplication operator - * - * @param that a hardware [[SInt]] - * @return the product of this $coll and `that` - * $sumWidth - * $singleCycleMul - * @group Arithmetic - */ - final def * (that: SInt): SInt = macro SourceInfoTransform.thatArg - /** @group SourceInfoTransformMacro */ - def do_* (that: SInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = that * this - - /** Addition operator (expanding width) - * - * @param that a hardware $coll - * @return the sum of this $coll and `that` - * $maxWidthPlusOne - * @group Arithmetic - */ - final def +& (that: UInt): UInt = macro SourceInfoTransform.thatArg - - /** Addition operator (constant width) - * - * @param that a hardware $coll - * @return the sum of this $coll and `that` - * $maxWidth - * @group Arithmetic - */ - final def +% (that: UInt): UInt = macro SourceInfoTransform.thatArg - - /** Subtraction operator (increasing width) - * - * @param that a hardware $coll - * @return the difference of this $coll less `that` - * $maxWidthPlusOne - * @group Arithmetic - */ - final def -& (that: UInt): UInt = macro SourceInfoTransform.thatArg - - /** Subtraction operator (constant width) - * - * @param that a hardware $coll - * @return the difference of this $coll less `that` - * $maxWidth - * @group Arithmetic - */ - final def -% (that: UInt): UInt = macro SourceInfoTransform.thatArg - - /** @group SourceInfoTransformMacro */ - def do_+& (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = - binop(sourceInfo, UInt((this.width max that.width) + 1), AddOp, that) - /** @group SourceInfoTransformMacro */ - def do_+% (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = - (this +& that).tail(1) - /** @group SourceInfoTransformMacro */ - def do_-& (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = - (this subtractAsSInt that).asUInt - /** @group SourceInfoTransformMacro */ - def do_-% (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = - (this subtractAsSInt that).tail(1) - - /** Bitwise and operator - * - * @param that a hardware $coll - * @return the bitwise and of this $coll and `that` - * $maxWidth - * @group Bitwise - */ - final def & (that: UInt): UInt = macro SourceInfoTransform.thatArg - - /** Bitwise or operator - * - * @param that a hardware $coll - * @return the bitwise or of this $coll and `that` - * $maxWidth - * @group Bitwise - */ - final def | (that: UInt): UInt = macro SourceInfoTransform.thatArg - - /** Bitwise exclusive or (xor) operator - * - * @param that a hardware $coll - * @return the bitwise xor of this $coll and `that` - * $maxWidth - * @group Bitwise - */ - final def ^ (that: UInt): UInt = macro SourceInfoTransform.thatArg - - // override def abs: UInt = macro SourceInfoTransform.noArg - def do_abs(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = this - - /** @group SourceInfoTransformMacro */ - def do_& (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = - binop(sourceInfo, UInt(this.width max that.width), BitAndOp, that) - /** @group SourceInfoTransformMacro */ - def do_| (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = - binop(sourceInfo, UInt(this.width max that.width), BitOrOp, that) - /** @group SourceInfoTransformMacro */ - def do_^ (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = - binop(sourceInfo, UInt(this.width max that.width), BitXorOp, that) - - /** @group SourceInfoTransformMacro */ - def do_unary_~ (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = - unop(sourceInfo, UInt(width = width), BitNotOp) - - // REVIEW TODO: Can these be defined on Bits? - /** Or reduction operator - * - * @return a hardware [[Bool]] resulting from every bit of this $coll or'd together - * @group Bitwise - */ - final def orR(): Bool = macro SourceInfoTransform.noArg - - /** And reduction operator - * - * @return a hardware [[Bool]] resulting from every bit of this $coll and'd together - * @group Bitwise - */ - final def andR(): Bool = macro SourceInfoTransform.noArg - - /** Exclusive or (xor) reduction operator - * - * @return a hardware [[Bool]] resulting from every bit of this $coll xor'd together - * @group Bitwise - */ - final def xorR(): Bool = macro SourceInfoTransform.noArg - - /** @group SourceInfoTransformMacro */ - def do_orR(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = redop(sourceInfo, OrReduceOp) - /** @group SourceInfoTransformMacro */ - def do_andR(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = redop(sourceInfo, AndReduceOp) - /** @group SourceInfoTransformMacro */ - def do_xorR(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = redop(sourceInfo, XorReduceOp) - - override def do_< (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, LessOp, that) - override def do_> (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, GreaterOp, that) - override def do_<= (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, LessEqOp, that) - override def do_>= (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, GreaterEqOp, that) - - @chiselRuntimeDeprecated - @deprecated("Use '=/=', which avoids potential precedence problems", "3.0") - final def != (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = this =/= that - - /** Dynamic not equals operator - * - * @param that a hardware $coll - * @return a hardware [[Bool]] asserted if this $coll is not equal to `that` - * @group Comparison - */ - final def =/= (that: UInt): Bool = macro SourceInfoTransform.thatArg - - /** Dynamic equals operator - * - * @param that a hardware $coll - * @return a hardware [[Bool]] asserted if this $coll is equal to `that` - * @group Comparison - */ - final def === (that: UInt): Bool = macro SourceInfoTransform.thatArg - - /** @group SourceInfoTransformMacro */ - def do_=/= (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, NotEqualOp, that) - /** @group SourceInfoTransformMacro */ - def do_=== (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, EqualOp, that) - - /** Unary not - * - * @return a hardware [[Bool]] asserted if this $coll equals zero - * @group Bitwise - */ - final def unary_! () : Bool = macro SourceInfoTransform.noArg - - /** @group SourceInfoTransformMacro */ - def do_unary_! (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions) : Bool = this === 0.U(1.W) - - override def do_<< (that: Int)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = - binop(sourceInfo, UInt(this.width + that), ShiftLeftOp, validateShiftAmount(that)) - override def do_<< (that: BigInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = - this << castToInt(that, "Shift amount") - override def do_<< (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = - binop(sourceInfo, UInt(this.width.dynamicShiftLeft(that.width)), DynamicShiftLeftOp, that) - override def do_>> (that: Int)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = - binop(sourceInfo, UInt(this.width.shiftRight(that)), ShiftRightOp, validateShiftAmount(that)) - override def do_>> (that: BigInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = - this >> castToInt(that, "Shift amount") - override def do_>> (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = - binop(sourceInfo, UInt(this.width), DynamicShiftRightOp, that) - - /** Conditionally set or clear a bit - * - * @param off a dynamic offset - * @param dat set if true, clear if false - * @return a hrdware $coll with bit `off` set or cleared based on the value of `dat` - * $unchangedWidth - */ - final def bitSet(off: UInt, dat: Bool): UInt = macro UIntTransform.bitset - - /** @group SourceInfoTransformMacro */ - def do_bitSet(off: UInt, dat: Bool)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = { - val bit = 1.U(1.W) << off - Mux(dat, this | bit, ~(~this | bit)) - } - - // TODO: this eventually will be renamed as toSInt, once the existing toSInt - // completes its deprecation phase. - /** Zero extend as [[SInt]] - * - * @return an [[SInt]] equal to this $coll with an additional zero in its most significant bit - * @note The width of the returned [[SInt]] is `width of this` + `1`. - */ - final def zext(): SInt = macro SourceInfoTransform.noArg - /** @group SourceInfoTransformMacro */ - def do_zext(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = - pushOp(DefPrim(sourceInfo, SInt(width + 1), ConvertOp, ref)) - - override def do_asSInt(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = - pushOp(DefPrim(sourceInfo, SInt(width), AsSIntOp, ref)) - override def do_asUInt(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = this - override def do_asFixedPoint(binaryPoint: BinaryPoint)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = { - binaryPoint match { - case KnownBinaryPoint(value) => - val iLit = ILit(value) - pushOp(DefPrim(sourceInfo, FixedPoint(width, binaryPoint), AsFixedPointOp, ref, iLit)) - case _ => - throwException(s"cannot call $this.asFixedPoint(binaryPoint=$binaryPoint), you must specify a known binaryPoint") - } - } - - override def do_asInterval(range: IntervalRange = IntervalRange.Unknown) - (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { - (range.lower, range.upper, range.binaryPoint) match { - case (lx: firrtlconstraint.IsKnown, ux: firrtlconstraint.IsKnown, KnownBinaryPoint(bp)) => - // No mechanism to pass open/close to firrtl so need to handle directly - val l = lx match { - case firrtlir.Open(x) => x + BigDecimal(1) / BigDecimal(BigInt(1) << bp) - case firrtlir.Closed(x) => x - } - val u = ux match { - case firrtlir.Open(x) => x - BigDecimal(1) / BigDecimal(BigInt(1) << bp) - case firrtlir.Closed(x) => x - } - val minBI = (l * BigDecimal(BigInt(1) << bp)).setScale(0, BigDecimal.RoundingMode.FLOOR).toBigIntExact.get - val maxBI = (u * BigDecimal(BigInt(1) << bp)).setScale(0, BigDecimal.RoundingMode.FLOOR).toBigIntExact.get - pushOp(DefPrim(sourceInfo, Interval(range), AsIntervalOp, ref, ILit(minBI), ILit(maxBI), ILit(bp))) - case _ => - throwException( - s"cannot call $this.asInterval($range), you must specify a known binaryPoint and range") - } - } - private[chisel3] override def connectFromBits(that: Bits)(implicit sourceInfo: SourceInfo, - compileOptions: CompileOptions): Unit = { - this := that.asUInt - } - - private def subtractAsSInt(that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = - binop(sourceInfo, SInt((this.width max that.width) + 1), SubOp, that) -} - -/** A data type for signed integers, represented as a binary bitvector. Defines arithmetic operations between other - * integer types. - * - * @define coll [[SInt]] - * @define numType $coll - * @define expandingWidth @note The width of the returned $coll is `width of this` + `1`. - * @define constantWidth @note The width of the returned $coll is unchanged, i.e., `width of this`. - */ -sealed class SInt private[chisel3] (width: Width) extends Bits(width) with Num[SInt] { - override def toString: String = { - val bindingString = litOption match { - case Some(value) => s"($value)" - case _ => bindingToString - } - s"SInt$width$bindingString" - } - - private[chisel3] override def typeEquivalent(that: Data): Boolean = - this.getClass == that.getClass && this.width == that.width // TODO: should this be true for unspecified widths? - - private[chisel3] override def cloneTypeWidth(w: Width): this.type = - new SInt(w).asInstanceOf[this.type] - - /** Unary negation (expanding width) - * - * @return a hardware $coll equal to zero minus this $coll - * $constantWidth - * @group Arithmetic - */ - final def unary_- (): SInt = macro SourceInfoTransform.noArg - - /** Unary negation (constant width) - * - * @return a hardware $coll equal to zero minus `this` shifted right by one - * $constantWidth - * @group Arithmetic - */ - final def unary_-% (): SInt = macro SourceInfoTransform.noArg - - /** @group SourceInfoTransformMacro */ - def unary_- (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = 0.S - this - /** @group SourceInfoTransformMacro */ - def unary_-% (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = 0.S -% this - - /** add (default - no growth) operator */ - override def do_+ (that: SInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = - this +% that - /** subtract (default - no growth) operator */ - override def do_- (that: SInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = - this -% that - override def do_* (that: SInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = - binop(sourceInfo, SInt(this.width + that.width), TimesOp, that) - override def do_/ (that: SInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = - binop(sourceInfo, SInt(this.width), DivideOp, that) - override def do_% (that: SInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = - binop(sourceInfo, SInt(this.width), RemOp, that) - - /** Multiplication operator - * - * @param that a hardware $coll - * @return the product of this $coll and `that` - * $sumWidth - * $singleCycleMul - * @group Arithmetic - */ - final def * (that: UInt): SInt = macro SourceInfoTransform.thatArg - /** @group SourceInfoTransformMacro */ - def do_* (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = { - val thatToSInt = that.zext() - val result = binop(sourceInfo, SInt(this.width + thatToSInt.width), TimesOp, thatToSInt) - result.tail(1).asSInt - } - - /** Addition operator (expanding width) - * - * @param that a hardware $coll - * @return the sum of this $coll and `that` - * $maxWidthPlusOne - * @group Arithmetic - */ - final def +& (that: SInt): SInt = macro SourceInfoTransform.thatArg - - /** Addition operator (constant width) - * - * @param that a hardware $coll - * @return the sum of this $coll and `that` shifted right by one - * $maxWidth - * @group Arithmetic - */ - final def +% (that: SInt): SInt = macro SourceInfoTransform.thatArg - - /** Subtraction operator (increasing width) - * - * @param that a hardware $coll - * @return the difference of this $coll less `that` - * $maxWidthPlusOne - * @group Arithmetic - */ - final def -& (that: SInt): SInt = macro SourceInfoTransform.thatArg - - /** Subtraction operator (constant width) - * - * @param that a hardware $coll - * @return the difference of this $coll less `that` shifted right by one - * $maxWidth - * @group Arithmetic - */ - final def -% (that: SInt): SInt = macro SourceInfoTransform.thatArg - - /** @group SourceInfoTransformMacro */ - def do_+& (that: SInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = - binop(sourceInfo, SInt((this.width max that.width) + 1), AddOp, that) - /** @group SourceInfoTransformMacro */ - def do_+% (that: SInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = - (this +& that).tail(1).asSInt - /** @group SourceInfoTransformMacro */ - def do_-& (that: SInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = - binop(sourceInfo, SInt((this.width max that.width) + 1), SubOp, that) - /** @group SourceInfoTransformMacro */ - def do_-% (that: SInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = - (this -& that).tail(1).asSInt - - /** Bitwise and operator - * - * @param that a hardware $coll - * @return the bitwise and of this $coll and `that` - * $maxWidth - * @group Bitwise - */ - final def & (that: SInt): SInt = macro SourceInfoTransform.thatArg - - /** Bitwise or operator - * - * @param that a hardware $coll - * @return the bitwise or of this $coll and `that` - * $maxWidth - * @group Bitwise - */ - final def | (that: SInt): SInt = macro SourceInfoTransform.thatArg - - /** Bitwise exclusive or (xor) operator - * - * @param that a hardware $coll - * @return the bitwise xor of this $coll and `that` - * $maxWidth - * @group Bitwise - */ - final def ^ (that: SInt): SInt = macro SourceInfoTransform.thatArg - - /** @group SourceInfoTransformMacro */ - def do_& (that: SInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = - binop(sourceInfo, UInt(this.width max that.width), BitAndOp, that).asSInt - /** @group SourceInfoTransformMacro */ - def do_| (that: SInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = - binop(sourceInfo, UInt(this.width max that.width), BitOrOp, that).asSInt - /** @group SourceInfoTransformMacro */ - def do_^ (that: SInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = - binop(sourceInfo, UInt(this.width max that.width), BitXorOp, that).asSInt - - /** @group SourceInfoTransformMacro */ - def do_unary_~ (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = - unop(sourceInfo, UInt(width = width), BitNotOp).asSInt - - override def do_< (that: SInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, LessOp, that) - override def do_> (that: SInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, GreaterOp, that) - override def do_<= (that: SInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, LessEqOp, that) - override def do_>= (that: SInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, GreaterEqOp, that) - - @chiselRuntimeDeprecated - @deprecated("Use '=/=', which avoids potential precedence problems", "3.0") - final def != (that: SInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = this =/= that - - /** Dynamic not equals operator - * - * @param that a hardware $coll - * @return a hardware [[Bool]] asserted if this $coll is not equal to `that` - * @group Comparison - */ - final def =/= (that: SInt): Bool = macro SourceInfoTransform.thatArg - - /** Dynamic equals operator - * - * @param that a hardware $coll - * @return a hardware [[Bool]] asserted if this $coll is equal to `that` - * @group Comparison - */ - final def === (that: SInt): Bool = macro SourceInfoTransform.thatArg - - /** @group SourceInfoTransformMacro */ - def do_=/= (that: SInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, NotEqualOp, that) - /** @group SourceInfoTransformMacro */ - def do_=== (that: SInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, EqualOp, that) - -// final def abs(): UInt = macro SourceInfoTransform.noArg - - def do_abs(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = { - Mux(this < 0.S, (-this), this) - } - - override def do_<< (that: Int)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = - binop(sourceInfo, SInt(this.width + that), ShiftLeftOp, validateShiftAmount(that)) - override def do_<< (that: BigInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = - this << castToInt(that, "Shift amount") - override def do_<< (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = - binop(sourceInfo, SInt(this.width.dynamicShiftLeft(that.width)), DynamicShiftLeftOp, that) - override def do_>> (that: Int)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = - binop(sourceInfo, SInt(this.width.shiftRight(that)), ShiftRightOp, validateShiftAmount(that)) - override def do_>> (that: BigInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = - this >> castToInt(that, "Shift amount") - override def do_>> (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = - binop(sourceInfo, SInt(this.width), DynamicShiftRightOp, that) - - override def do_asUInt(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = pushOp(DefPrim(sourceInfo, UInt(this.width), AsUIntOp, ref)) - override def do_asSInt(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = this - override def do_asFixedPoint(binaryPoint: BinaryPoint)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = { - binaryPoint match { - case KnownBinaryPoint(value) => - val iLit = ILit(value) - pushOp(DefPrim(sourceInfo, FixedPoint(width, binaryPoint), AsFixedPointOp, ref, iLit)) - case _ => - throwException(s"cannot call $this.asFixedPoint(binaryPoint=$binaryPoint), you must specify a known binaryPoint") - } - } - - override def do_asInterval(range: IntervalRange = IntervalRange.Unknown) - (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { - (range.lower, range.upper, range.binaryPoint) match { - case (lx: firrtlconstraint.IsKnown, ux: firrtlconstraint.IsKnown, KnownBinaryPoint(bp)) => - // No mechanism to pass open/close to firrtl so need to handle directly - val l = lx match { - case firrtlir.Open(x) => x + BigDecimal(1) / BigDecimal(BigInt(1) << bp) - case firrtlir.Closed(x) => x - } - val u = ux match { - case firrtlir.Open(x) => x - BigDecimal(1) / BigDecimal(BigInt(1) << bp) - case firrtlir.Closed(x) => x - } - //TODO: (chick) Need to determine, what asInterval needs, and why it might need min and max as args -- CAN IT BE UNKNOWN? - // Angie's operation: Decimal -> Int -> Decimal loses information. Need to be conservative here? - val minBI = (l * BigDecimal(BigInt(1) << bp)).setScale(0, BigDecimal.RoundingMode.FLOOR).toBigIntExact.get - val maxBI = (u * BigDecimal(BigInt(1) << bp)).setScale(0, BigDecimal.RoundingMode.FLOOR).toBigIntExact.get - pushOp(DefPrim(sourceInfo, Interval(range), AsIntervalOp, ref, ILit(minBI), ILit(maxBI), ILit(bp))) - case _ => - throwException( - s"cannot call $this.asInterval($range), you must specify a known binaryPoint and range") - } - } - - private[chisel3] override def connectFromBits(that: Bits)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions) { - this := that.asSInt - } -} - -sealed trait Reset extends Element with ToBoolable { - /** Casts this $coll to an [[AsyncReset]] */ - final def asAsyncReset(): AsyncReset = macro SourceInfoWhiteboxTransform.noArg - - /** @group SourceInfoTransformMacro */ - def do_asAsyncReset(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): AsyncReset -} - -object Reset { - def apply(): Reset = new ResetType -} - -/** "Abstract" Reset Type inferred in FIRRTL to either [[AsyncReset]] or [[Bool]] - * - * @note This shares a common interface with [[AsyncReset]] and [[Bool]] but is not their actual - * super type due to Bool inheriting from abstract class UInt - */ -final class ResetType(private[chisel3] val width: Width = Width(1)) extends Element with Reset { - override def toString: String = s"Reset$bindingToString" - - def cloneType: this.type = Reset().asInstanceOf[this.type] - - private[chisel3] def typeEquivalent(that: Data): Boolean = - this.getClass == that.getClass - - override def connect(that: Data)(implicit sourceInfo: SourceInfo, connectCompileOptions: CompileOptions): Unit = that match { - case _: Reset | DontCare => super.connect(that)(sourceInfo, connectCompileOptions) - case _ => super.badConnect(that)(sourceInfo) - } - - override def litOption = None - - /** Not really supported */ - def toPrintable: Printable = PString("Reset") - - override def do_asUInt(implicit sourceInfo: SourceInfo, connectCompileOptions: CompileOptions): UInt = pushOp(DefPrim(sourceInfo, UInt(this.width), AsUIntOp, ref)) - - private[chisel3] override def connectFromBits(that: Bits)(implicit sourceInfo: SourceInfo, - compileOptions: CompileOptions): Unit = { - this := that - } - - /** @group SourceInfoTransformMacro */ - def do_asAsyncReset(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): AsyncReset = - pushOp(DefPrim(sourceInfo, AsyncReset(), AsAsyncResetOp, ref)) - - /** @group SourceInfoTransformMacro */ - def do_asBool(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = - pushOp(DefPrim(sourceInfo, Bool(), AsUIntOp, ref)) - - /** @group SourceInfoTransformMacro */ - def do_toBool(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = do_asBool -} - -object AsyncReset { - def apply(): AsyncReset = new AsyncReset -} - -/** Data type representing asynchronous reset signals - * - * These signals are similar to [[Clock]]s in that they must be glitch-free for proper circuit - * operation. [[Reg]]s defined with the implicit reset being an [[AsyncReset]] will be - * asychronously reset registers. - */ -sealed class AsyncReset(private[chisel3] val width: Width = Width(1)) extends Element with Reset { - override def toString: String = s"AsyncReset$bindingToString" - - def cloneType: this.type = AsyncReset().asInstanceOf[this.type] - - private[chisel3] def typeEquivalent(that: Data): Boolean = - this.getClass == that.getClass - - override def connect(that: Data)(implicit sourceInfo: SourceInfo, connectCompileOptions: CompileOptions): Unit = that match { - case _: AsyncReset | DontCare => super.connect(that)(sourceInfo, connectCompileOptions) - case _ => super.badConnect(that)(sourceInfo) - } - - override def litOption = None - - /** Not really supported */ - def toPrintable: Printable = PString("AsyncReset") - - override def do_asUInt(implicit sourceInfo: SourceInfo, connectCompileOptions: CompileOptions): UInt = pushOp(DefPrim(sourceInfo, UInt(this.width), AsUIntOp, ref)) - - // TODO Is this right? - private[chisel3] override def connectFromBits(that: Bits)(implicit sourceInfo: SourceInfo, - compileOptions: CompileOptions): Unit = { - this := that.asBool.asAsyncReset - } - - /** @group SourceInfoTransformMacro */ - def do_asAsyncReset(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): AsyncReset = this - - /** @group SourceInfoTransformMacro */ - def do_asBool(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = - pushOp(DefPrim(sourceInfo, Bool(), AsUIntOp, ref)) - - /** @group SourceInfoTransformMacro */ - def do_toBool(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = do_asBool -} - -// REVIEW TODO: Why does this extend UInt and not Bits? Does defining airth -// operations on a Bool make sense? -/** A data type for booleans, defined as a single bit indicating true or false. - * - * @define coll [[Bool]] - * @define numType $coll - */ -sealed class Bool() extends UInt(1.W) with Reset { - override def toString: String = { - val bindingString = litToBooleanOption match { - case Some(value) => s"($value)" - case _ => bindingToString - } - s"Bool$bindingString" - } - - private[chisel3] override def cloneTypeWidth(w: Width): this.type = { - require(!w.known || w.get == 1) - new Bool().asInstanceOf[this.type] - } - - /** Convert to a [[scala.Option]] of [[scala.Boolean]] */ - def litToBooleanOption: Option[Boolean] = litOption.map { - case intVal if intVal == 1 => true - case intVal if intVal == 0 => false - case intVal => throwException(s"Boolean with unexpected literal value $intVal") - } - - /** Convert to a [[scala.Boolean]] */ - def litToBoolean: Boolean = litToBooleanOption.get - - // REVIEW TODO: Why does this need to exist and have different conventions - // than Bits? - - /** Bitwise and operator - * - * @param that a hardware $coll - * @return the bitwise and of this $coll and `that` - * @group Bitwise - */ - final def & (that: Bool): Bool = macro SourceInfoTransform.thatArg - - /** Bitwise or operator - * - * @param that a hardware $coll - * @return the bitwise or of this $coll and `that` - * @group Bitwise - */ - final def | (that: Bool): Bool = macro SourceInfoTransform.thatArg - - /** Bitwise exclusive or (xor) operator - * - * @param that a hardware $coll - * @return the bitwise xor of this $coll and `that` - * @group Bitwise - */ - final def ^ (that: Bool): Bool = macro SourceInfoTransform.thatArg - - /** @group SourceInfoTransformMacro */ - def do_& (that: Bool)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = - binop(sourceInfo, Bool(), BitAndOp, that) - /** @group SourceInfoTransformMacro */ - def do_| (that: Bool)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = - binop(sourceInfo, Bool(), BitOrOp, that) - /** @group SourceInfoTransformMacro */ - def do_^ (that: Bool)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = - binop(sourceInfo, Bool(), BitXorOp, that) - - /** @group SourceInfoTransformMacro */ - override def do_unary_~ (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = - unop(sourceInfo, Bool(), BitNotOp) - - /** Logical or operator - * - * @param that a hardware $coll - * @return the lgocial or of this $coll and `that` - * @note this is equivalent to [[Bool!.|(that:chisel3\.Bool)* Bool.|)]] - * @group Logical - */ - def || (that: Bool): Bool = macro SourceInfoTransform.thatArg - - /** @group SourceInfoTransformMacro */ - def do_|| (that: Bool)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = this | that - - /** Logical and operator - * - * @param that a hardware $coll - * @return the lgocial and of this $coll and `that` - * @note this is equivalent to [[Bool!.&(that:chisel3\.Bool)* Bool.&]] - * @group Logical - */ - def && (that: Bool): Bool = macro SourceInfoTransform.thatArg - - /** @group SourceInfoTransformMacro */ - def do_&& (that: Bool)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = this & that - - /** Reinterprets this $coll as a clock */ - def asClock(): Clock = macro SourceInfoTransform.noArg - - /** @group SourceInfoTransformMacro */ - def do_asClock(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Clock = pushOp(DefPrim(sourceInfo, Clock(), AsClockOp, ref)) - - /** @group SourceInfoTransformMacro */ - def do_asAsyncReset(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): AsyncReset = - pushOp(DefPrim(sourceInfo, AsyncReset(), AsAsyncResetOp, ref)) -} - -package experimental { - - import chisel3.internal.firrtl.BinaryPoint - - /** Chisel types that have binary points support retrieving - * literal values as `Double` or `BigDecimal` - */ - trait HasBinaryPoint { self: Bits => - def binaryPoint: BinaryPoint - - /** Return the [[Double]] value of this instance if it is a Literal - * @note this method may throw an exception if the literal value won't fit in a Double - */ - def litToDoubleOption: Option[Double] = { - litOption match { - case Some(bigInt: BigInt) => - Some(Num.toDouble(bigInt, binaryPoint)) - case _ => None - } - } - - /** Return the double value of this instance assuming it is a literal (convenience method) - */ - def litToDouble: Double = litToDoubleOption.get - - /** Return the [[BigDecimal]] value of this instance if it is a Literal - * @note this method may throw an exception if the literal value won't fit in a BigDecimal - */ - def litToBigDecimalOption: Option[BigDecimal] = { - litOption match { - case Some(bigInt: BigInt) => - Some(Num.toBigDecimal(bigInt, binaryPoint)) - case _ => None - } - } - - /** Return the [[BigDecimal]] value of this instance assuming it is a literal (convenience method) - * @return - */ - def litToBigDecimal: BigDecimal = litToBigDecimalOption.get - } - //scalastyle:off number.of.methods - /** A sealed class representing a fixed point number that has a bit width and a binary point The width and binary point - * may be inferred. - * - * IMPORTANT: The API provided here is experimental and may change in the future. - * - * @param width bit width of the fixed point number - * @param binaryPoint the position of the binary point with respect to the right most bit of the width currently this - * should be positive but it is hoped to soon support negative points and thus use this field as a - * simple exponent - * @define coll [[FixedPoint]] - * @define numType $coll - * @define expandingWidth @note The width of the returned $coll is `width of this` + `1`. - * @define constantWidth @note The width of the returned $coll is unchanged, i.e., `width of this`. - */ - sealed class FixedPoint private(width: Width, val binaryPoint: BinaryPoint) - extends Bits(width) with Num[FixedPoint] with HasBinaryPoint { - - override def toString: String = { - val bindingString = litToDoubleOption match { - case Some(value) => s"($value)" - case _ => bindingToString - } - s"FixedPoint$width$binaryPoint$bindingString" - } - - private[chisel3] override def typeEquivalent(that: Data): Boolean = that match { - case that: FixedPoint => this.width == that.width && this.binaryPoint == that.binaryPoint // TODO: should this be true for unspecified widths? - case _ => false - } - - private[chisel3] override def cloneTypeWidth(w: Width): this.type = - new FixedPoint(w, binaryPoint).asInstanceOf[this.type] - - override def connect(that: Data)(implicit sourceInfo: SourceInfo, connectCompileOptions: CompileOptions): Unit = that match { - case _: FixedPoint|DontCare => super.connect(that) - case _ => this badConnect that - } - - /** Unary negation (expanding width) - * - * @return a hardware $coll equal to zero minus this $coll - * $expandingWidth - * @group Arithmetic - */ - final def unary_- (): FixedPoint = macro SourceInfoTransform.noArg - - /** Unary negation (constant width) - * - * @return a hardware $coll equal to zero minus `this` shifted right by one - * $constantWidth - * @group Arithmetic - */ - final def unary_-% (): FixedPoint = macro SourceInfoTransform.noArg - - /** @group SourceInfoTransformMacro */ - def unary_- (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = FixedPoint.fromBigInt(0) - this - /** @group SourceInfoTransformMacro */ - def unary_-% (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = FixedPoint.fromBigInt(0) -% this - - /** add (default - no growth) operator */ - override def do_+ (that: FixedPoint)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = - this +% that - /** subtract (default - no growth) operator */ - override def do_- (that: FixedPoint)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = - this -% that - override def do_* (that: FixedPoint)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = - binop(sourceInfo, FixedPoint(this.width + that.width, this.binaryPoint + that.binaryPoint), TimesOp, that) - override def do_/ (that: FixedPoint)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = - throwException(s"division is illegal on FixedPoint types") - override def do_% (that: FixedPoint)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = - throwException(s"mod is illegal on FixedPoint types") - - - /** Multiplication operator - * - * @param that a hardware [[UInt]] - * @return the product of this $coll and `that` - * $sumWidth - * $singleCycleMul - * @group Arithmetic - */ - final def * (that: UInt): FixedPoint = macro SourceInfoTransform.thatArg - /** @group SourceInfoTransformMacro */ - def do_* (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = - binop(sourceInfo, FixedPoint(this.width + that.width, binaryPoint), TimesOp, that) - - /** Multiplication operator - * - * @param that a hardware [[SInt]] - * @return the product of this $coll and `that` - * $sumWidth - * $singleCycleMul - * @group Arithmetic - */ - final def * (that: SInt): FixedPoint = macro SourceInfoTransform.thatArg - /** @group SourceInfoTransformMacro */ - def do_* (that: SInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = - binop(sourceInfo, FixedPoint(this.width + that.width, binaryPoint), TimesOp, that) - - /** Addition operator (expanding width) - * - * @param that a hardware $coll - * @return the sum of this $coll and `that` - * $maxWidthPlusOne - * @group Arithmetic - */ - final def +& (that: FixedPoint): FixedPoint = macro SourceInfoTransform.thatArg - - /** Addition operator (constant width) - * - * @param that a hardware $coll - * @return the sum of this $coll and `that` shifted right by one - * $maxWidth - * @group Arithmetic - */ - final def +% (that: FixedPoint): FixedPoint = macro SourceInfoTransform.thatArg - - /** Subtraction operator (increasing width) - * - * @param that a hardware $coll - * @return the difference of this $coll less `that` - * $maxWidthPlusOne - * @group Arithmetic - */ - final def -& (that: FixedPoint): FixedPoint = macro SourceInfoTransform.thatArg - - /** Subtraction operator (constant width) - * - * @param that a hardware $coll - * @return the difference of this $coll less `that` shifted right by one - * $maxWidth - * @group Arithmetic - */ - final def -% (that: FixedPoint): FixedPoint = macro SourceInfoTransform.thatArg - - /** @group SourceInfoTransformMacro */ - def do_+& (that: FixedPoint)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = { - (this.width, that.width, this.binaryPoint, that.binaryPoint) match { - case (KnownWidth(thisWidth), KnownWidth(thatWidth), KnownBinaryPoint(thisBP), KnownBinaryPoint(thatBP)) => - val thisIntWidth = thisWidth - thisBP - val thatIntWidth = thatWidth - thatBP - val newBinaryPoint = thisBP max thatBP - val newWidth = (thisIntWidth max thatIntWidth) + newBinaryPoint + 1 - binop(sourceInfo, FixedPoint(newWidth.W, newBinaryPoint.BP), AddOp, that) - case _ => - val newBinaryPoint = this.binaryPoint max that.binaryPoint - binop(sourceInfo, FixedPoint(UnknownWidth(), newBinaryPoint), AddOp, that) - } - } - - /** @group SourceInfoTransformMacro */ - def do_+% (that: FixedPoint)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = - (this +& that).tail(1).asFixedPoint(this.binaryPoint max that.binaryPoint) - /** @group SourceInfoTransformMacro */ - def do_-& (that: FixedPoint)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = { - (this.width, that.width, this.binaryPoint, that.binaryPoint) match { - case (KnownWidth(thisWidth), KnownWidth(thatWidth), KnownBinaryPoint(thisBP), KnownBinaryPoint(thatBP)) => - val thisIntWidth = thisWidth - thisBP - val thatIntWidth = thatWidth - thatBP - val newBinaryPoint = thisBP max thatBP - val newWidth = (thisIntWidth max thatIntWidth) + newBinaryPoint + 1 - binop(sourceInfo, FixedPoint(newWidth.W, newBinaryPoint.BP), SubOp, that) - case _ => - val newBinaryPoint = this.binaryPoint max that.binaryPoint - binop(sourceInfo, FixedPoint(UnknownWidth(), newBinaryPoint), SubOp, that) - } - } - - /** @group SourceInfoTransformMacro */ - def do_-% (that: FixedPoint)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = - (this -& that).tail(1).asFixedPoint(this.binaryPoint max that.binaryPoint) - - /** Bitwise and operator - * - * @param that a hardware $coll - * @return the bitwise and of this $coll and `that` - * $maxWidth - * @group Bitwise - */ - final def & (that: FixedPoint): FixedPoint = macro SourceInfoTransform.thatArg - - /** Bitwise or operator - * - * @param that a hardware $coll - * @return the bitwise or of this $coll and `that` - * $maxWidth - * @group Bitwise - */ - final def | (that: FixedPoint): FixedPoint = macro SourceInfoTransform.thatArg - - /** Bitwise exclusive or (xor) operator - * - * @param that a hardware $coll - * @return the bitwise xor of this $coll and `that` - * $maxWidth - * @group Bitwise - */ - final def ^ (that: FixedPoint): FixedPoint = macro SourceInfoTransform.thatArg - - /** @group SourceInfoTransformMacro */ - def do_& (that: FixedPoint)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = - throwException(s"And is illegal between $this and $that") - /** @group SourceInfoTransformMacro */ - def do_| (that: FixedPoint)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = - throwException(s"Or is illegal between $this and $that") - /** @group SourceInfoTransformMacro */ - def do_^ (that: FixedPoint)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = - throwException(s"Xor is illegal between $this and $that") - - final def setBinaryPoint(that: Int): FixedPoint = macro SourceInfoTransform.thatArg - - /** @group SourceInfoTransformMacro */ - def do_setBinaryPoint(that: Int)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = this.binaryPoint match { - case KnownBinaryPoint(value) => - binop(sourceInfo, FixedPoint(this.width + (that - value), KnownBinaryPoint(that)), SetBinaryPoint, that) - case _ => - binop(sourceInfo, FixedPoint(UnknownWidth(), KnownBinaryPoint(that)), SetBinaryPoint, that) - } - - /** @group SourceInfoTransformMacro */ - def do_unary_~ (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = - throwException(s"Not is illegal on $this") - - override def do_< (that: FixedPoint)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, LessOp, that) - override def do_> (that: FixedPoint)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, GreaterOp, that) - override def do_<= (that: FixedPoint)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, LessEqOp, that) - override def do_>= (that: FixedPoint)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, GreaterEqOp, that) - - final def != (that: FixedPoint): Bool = macro SourceInfoTransform.thatArg - - /** Dynamic not equals operator - * - * @param that a hardware $coll - * @return a hardware [[Bool]] asserted if this $coll is not equal to `that` - * @group Comparison - */ - final def =/= (that: FixedPoint): Bool = macro SourceInfoTransform.thatArg - - /** Dynamic equals operator - * - * @param that a hardware $coll - * @return a hardware [[Bool]] asserted if this $coll is equal to `that` - * @group Comparison - */ - final def === (that: FixedPoint): Bool = macro SourceInfoTransform.thatArg - - /** @group SourceInfoTransformMacro */ - def do_!= (that: FixedPoint)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, NotEqualOp, that) - /** @group SourceInfoTransformMacro */ - def do_=/= (that: FixedPoint)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, NotEqualOp, that) - /** @group SourceInfoTransformMacro */ - def do_=== (that: FixedPoint)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, EqualOp, that) - - def do_abs(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = { - // TODO: remove this once we have CompileOptions threaded through the macro system. - import chisel3.ExplicitCompileOptions.NotStrict - Mux(this < 0.F(0.BP), 0.F(0.BP) - this, this) - } - - override def do_<< (that: Int)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = - binop(sourceInfo, FixedPoint(this.width + that, this.binaryPoint), ShiftLeftOp, validateShiftAmount(that)) - override def do_<< (that: BigInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = - (this << castToInt(that, "Shift amount")).asFixedPoint(this.binaryPoint) - override def do_<< (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = - binop(sourceInfo, FixedPoint(this.width.dynamicShiftLeft(that.width), this.binaryPoint), DynamicShiftLeftOp, that) - override def do_>> (that: Int)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = - binop(sourceInfo, FixedPoint(this.width.shiftRight(that), this.binaryPoint), ShiftRightOp, validateShiftAmount(that)) - override def do_>> (that: BigInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = - (this >> castToInt(that, "Shift amount")).asFixedPoint(this.binaryPoint) - override def do_>> (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = - binop(sourceInfo, FixedPoint(this.width, this.binaryPoint), DynamicShiftRightOp, that) - - override def do_asUInt(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = pushOp(DefPrim(sourceInfo, UInt(this.width), AsUIntOp, ref)) - override def do_asSInt(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = pushOp(DefPrim(sourceInfo, SInt(this.width), AsSIntOp, ref)) - - override def do_asFixedPoint(binaryPoint: BinaryPoint)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = { - binaryPoint match { - case KnownBinaryPoint(value) => - val iLit = ILit(value) - pushOp(DefPrim(sourceInfo, FixedPoint(width, binaryPoint), AsFixedPointOp, ref, iLit)) - case _ => - throwException(s"cannot call $this.asFixedPoint(binaryPoint=$binaryPoint), you must specify a known binaryPoint") - } - } - - def do_asInterval(binaryPoint: BinaryPoint)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { - throwException(s"cannot call $this.asInterval(binaryPoint=$binaryPoint), you must specify a range") - } - - override def do_asInterval(range: IntervalRange = IntervalRange.Unknown) - (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { - (range.lower, range.upper, range.binaryPoint) match { - case (lx: firrtlconstraint.IsKnown, ux: firrtlconstraint.IsKnown, KnownBinaryPoint(bp)) => - // No mechanism to pass open/close to firrtl so need to handle directly - val l = lx match { - case firrtlir.Open(x) => x + BigDecimal(1) / BigDecimal(BigInt(1) << bp) - case firrtlir.Closed(x) => x - } - val u = ux match { - case firrtlir.Open(x) => x - BigDecimal(1) / BigDecimal(BigInt(1) << bp) - case firrtlir.Closed(x) => x - } - val minBI = (l * BigDecimal(BigInt(1) << bp)).setScale(0, BigDecimal.RoundingMode.FLOOR).toBigIntExact.get - val maxBI = (u * BigDecimal(BigInt(1) << bp)).setScale(0, BigDecimal.RoundingMode.FLOOR).toBigIntExact.get - pushOp(DefPrim(sourceInfo, Interval(range), AsIntervalOp, ref, ILit(minBI), ILit(maxBI), ILit(bp))) - case _ => - throwException( - s"cannot call $this.asInterval($range), you must specify a known binaryPoint and range") - } - } - - private[chisel3] override def connectFromBits(that: Bits)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions) { - // TODO: redefine as just asFixedPoint on that, where FixedPoint.asFixedPoint just works. - this := (that match { - case fp: FixedPoint => fp.asSInt.asFixedPoint(this.binaryPoint) - case _ => that.asFixedPoint(this.binaryPoint) - }) - } - } - - /** Use PrivateObject to force users to specify width and binaryPoint by name - */ - sealed trait PrivateType - private case object PrivateObject extends PrivateType - - /** - * Factory and convenience methods for the FixedPoint class - * IMPORTANT: The API provided here is experimental and may change in the future. - */ - object FixedPoint extends NumObject { - - import FixedPoint.Implicits._ - - /** Create an FixedPoint type with inferred width. */ - def apply(): FixedPoint = apply(Width(), BinaryPoint()) - - /** Create an FixedPoint type or port with fixed width. */ - def apply(width: Width, binaryPoint: BinaryPoint): FixedPoint = new FixedPoint(width, binaryPoint) - - /** Create an FixedPoint literal with inferred width from BigInt. - * Use PrivateObject to force users to specify width and binaryPoint by name - */ - def fromBigInt(value: BigInt, width: Width, binaryPoint: BinaryPoint): FixedPoint = { - apply(value, width, binaryPoint) - } - /** Create an FixedPoint literal with inferred width from BigInt. - * Use PrivateObject to force users to specify width and binaryPoint by name - */ - def fromBigInt(value: BigInt, binaryPoint: BinaryPoint = 0.BP): FixedPoint = { - apply(value, Width(), binaryPoint) - } - /** Create an FixedPoint literal with inferred width from BigInt. - * Use PrivateObject to force users to specify width and binaryPoint by name - */ - def fromBigInt(value: BigInt, width: Int, binaryPoint: Int): FixedPoint = - if(width == -1) { - apply(value, Width(), BinaryPoint(binaryPoint)) - } - else { - apply(value, Width(width), BinaryPoint(binaryPoint)) - } - /** Create an FixedPoint literal with inferred width from Double. - * Use PrivateObject to force users to specify width and binaryPoint by name - */ - def fromDouble(value: Double, width: Width, binaryPoint: BinaryPoint): FixedPoint = { - fromBigInt( - toBigInt(value, binaryPoint.get), width = width, binaryPoint = binaryPoint - ) - } - /** Create an FixedPoint literal with inferred width from BigDecimal. - * Use PrivateObject to force users to specify width and binaryPoint by name - */ - def fromBigDecimal(value: BigDecimal, width: Width, binaryPoint: BinaryPoint): FixedPoint = { - fromBigInt( - toBigInt(value, binaryPoint.get), width = width, binaryPoint = binaryPoint - ) - } - - /** Create an FixedPoint port with specified width and binary position. */ - def apply(value: BigInt, width: Width, binaryPoint: BinaryPoint): FixedPoint = { - val lit = FPLit(value, width, binaryPoint) - val newLiteral = new FixedPoint(lit.width, lit.binaryPoint) - // Ensure we have something capable of generating a name. - lit.bindLitArg(newLiteral) - } - - - - object Implicits { - - implicit class fromDoubleToLiteral(double: Double) { - def F(binaryPoint: BinaryPoint): FixedPoint = { - FixedPoint.fromDouble(double, Width(), binaryPoint) - } - - def F(width: Width, binaryPoint: BinaryPoint): FixedPoint = { - FixedPoint.fromDouble(double, width, binaryPoint) - } - } - - implicit class fromBigDecimalToLiteral(bigDecimal: BigDecimal) { - def F(binaryPoint: BinaryPoint): FixedPoint = { - FixedPoint.fromBigDecimal(bigDecimal, Width(), binaryPoint) - } - - def F(width: Width, binaryPoint: BinaryPoint): FixedPoint = { - FixedPoint.fromBigDecimal(bigDecimal, width, binaryPoint) - } - } - } - } - - //scalastyle:off number.of.methods cyclomatic.complexity - /** - * A sealed class representing a fixed point number that has a range, an additional - * parameter that can determine a minimum and maximum supported value. - * The range can be used to reduce the required widths particularly in primitive - * operations with other Intervals, the canonical example being - * {{{ - * val one = 1.I - * val six = Seq.fill(6)(one).reduce(_ + _) - * }}} - * A UInt computed in this way would require a [[Width]] - * binary point - * The width and binary point may be inferred. - * - * IMPORTANT: The API provided here is experimental and may change in the future. - * - * @param range a range specifies min, max and binary point - */ - sealed class Interval private[chisel3] (val range: chisel3.internal.firrtl.IntervalRange) - extends Bits(range.getWidth) with Num[Interval] with HasBinaryPoint { - - override def toString: String = { - val bindingString = litOption match { - case Some(value) => s"($value)" - case _ => bindingToString - } - s"Interval$width$bindingString" - } - - private[chisel3] override def cloneTypeWidth(w: Width): this.type = - new Interval(range).asInstanceOf[this.type] - - //scalastyle:off cyclomatic.complexity - def toType: String = { - val zdec1 = """([+\-]?[0-9]\d*)(\.[0-9]*[1-9])(0*)""".r - val zdec2 = """([+\-]?[0-9]\d*)(\.0*)""".r - val dec = """([+\-]?[0-9]\d*)(\.[0-9]\d*)""".r - val int = """([+\-]?[0-9]\d*)""".r - def dec2string(v: BigDecimal): String = v.toString match { - case zdec1(x, y, z) => x + y - case zdec2(x, y) => x - case other => other - } - - val lowerString = range.lower match { - case firrtlir.Open(l) => s"(${dec2string(l)}, " - case firrtlir.Closed(l) => s"[${dec2string(l)}, " - case firrtlir.UnknownBound => s"[?, " - case _ => s"[?, " - } - val upperString = range.upper match { - case firrtlir.Open(u) => s"${dec2string(u)})" - case firrtlir.Closed(u) => s"${dec2string(u)}]" - case firrtlir.UnknownBound => s"?]" - case _ => s"?]" - } - val bounds = lowerString + upperString - - val pointString = range.binaryPoint match { - case KnownBinaryPoint(i) => "." + i.toString - case _ => "" - } - "Interval" + bounds + pointString - } - - private[chisel3] override def typeEquivalent(that: Data): Boolean = - that.isInstanceOf[Interval] && this.width == that.width - - def binaryPoint: BinaryPoint = range.binaryPoint - - override def connect(that: Data)(implicit sourceInfo: SourceInfo, connectCompileOptions: CompileOptions): Unit = { - that match { - case _: Interval|DontCare => super.connect(that) - case _ => this badConnect that - } - } - - final def unary_-(): Interval = macro SourceInfoTransform.noArg - final def unary_-%(): Interval = macro SourceInfoTransform.noArg - - def unary_-(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { - Interval.Zero - this - } - def unary_-%(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { - Interval.Zero -% this - } - - /** add (default - growing) operator */ - override def do_+(that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = - this +& that - /** subtract (default - growing) operator */ - override def do_-(that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = - this -& that - override def do_*(that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = - binop(sourceInfo, Interval(this.range * that.range), TimesOp, that) - - override def do_/(that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = - throwException(s"division is illegal on Interval types") - override def do_%(that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = - throwException(s"mod is illegal on Interval types") - - /** add (width +1) operator */ - final def +&(that: Interval): Interval = macro SourceInfoTransform.thatArg - /** add (no growth) operator */ - final def +%(that: Interval): Interval = macro SourceInfoTransform.thatArg - /** subtract (width +1) operator */ - final def -&(that: Interval): Interval = macro SourceInfoTransform.thatArg - /** subtract (no growth) operator */ - final def -%(that: Interval): Interval = macro SourceInfoTransform.thatArg - - def do_+&(that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { - binop(sourceInfo, Interval(this.range +& that.range), AddOp, that) - } - - def do_+%(that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { - throwException(s"Non-growing addition is not supported on Intervals: ${sourceInfo}") - } - - def do_-&(that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { - binop(sourceInfo, Interval(this.range -& that.range), SubOp, that) - } - - def do_-%(that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { - throwException(s"Non-growing subtraction is not supported on Intervals: ${sourceInfo}, try squeeze") - } - - final def &(that: Interval): Interval = macro SourceInfoTransform.thatArg - final def |(that: Interval): Interval = macro SourceInfoTransform.thatArg - final def ^(that: Interval): Interval = macro SourceInfoTransform.thatArg - - def do_&(that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = - throwException(s"And is illegal between $this and $that") - def do_|(that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = - throwException(s"Or is illegal between $this and $that") - def do_^(that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = - throwException(s"Xor is illegal between $this and $that") - - final def setPrecision(that: Int): Interval = macro SourceInfoTransform.thatArg - - // Precision change changes range -- see firrtl PrimOps (requires floor) - // aaa.bbb -> aaa.bb for sbp(2) - def do_setPrecision(that: Int)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { - val newBinaryPoint = BinaryPoint(that) - val newIntervalRange = this.range.setPrecision(newBinaryPoint) - binop(sourceInfo, Interval(newIntervalRange), SetBinaryPoint, that) - } - - /** Increase the precision of this Interval, moves the binary point to the left. - * aaa.bbb -> aaa.bbb00 - * @param that how many bits to shift binary point - * @return - */ - final def increasePrecision(that: Int): Interval = macro SourceInfoTransform.thatArg - - def do_increasePrecision(that: Int)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { - assert(that > 0, s"Must increase precision by an integer greater than zero.") - val newBinaryPoint = BinaryPoint(that) - val newIntervalRange = this.range.incPrecision(newBinaryPoint) - binop(sourceInfo, Interval(newIntervalRange), IncreasePrecision, that) - } - - /** Decrease the precision of this Interval, moves the binary point to the right. - * aaa.bbb -> aaa.b - * - * @param that number of bits to move binary point - * @return - */ - final def decreasePrecision(that: Int): Interval = macro SourceInfoTransform.thatArg - - def do_decreasePrecision(that: Int)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { - assert(that > 0, s"Must decrease precision by an integer greater than zero.") - val newBinaryPoint = BinaryPoint(that) - val newIntervalRange = this.range.decPrecision(newBinaryPoint) - binop(sourceInfo, Interval(newIntervalRange), DecreasePrecision, that) - } - - /** Returns this wire bitwise-inverted. */ - def do_unary_~ (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = - throwException(s"Not is illegal on $this") - - override def do_< (that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, LessOp, that) - override def do_> (that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, GreaterOp, that) - override def do_<= (that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, LessEqOp, that) - override def do_>= (that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, GreaterEqOp, that) - - final def != (that: Interval): Bool = macro SourceInfoTransform.thatArg - final def =/= (that: Interval): Bool = macro SourceInfoTransform.thatArg - final def === (that: Interval): Bool = macro SourceInfoTransform.thatArg - - def do_!= (that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, NotEqualOp, that) - def do_=/= (that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, NotEqualOp, that) - def do_=== (that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, EqualOp, that) - - // final def abs(): UInt = macro SourceInfoTransform.noArg - - def do_abs(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { - Mux(this < Interval.Zero, (Interval.Zero - this), this) - } - - override def do_<< (that: Int)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = - binop(sourceInfo, Interval(this.range << that), ShiftLeftOp, that) - - override def do_<< (that: BigInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = - do_<<(that.toInt) - - override def do_<< (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { - binop(sourceInfo, Interval(this.range << that), DynamicShiftLeftOp, that) - } - - override def do_>> (that: Int)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { - binop(sourceInfo, Interval(this.range >> that), ShiftRightOp, that) - } - - override def do_>> (that: BigInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = - do_>>(that.toInt) - - override def do_>> (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { - binop(sourceInfo, Interval(this.range >> that), DynamicShiftRightOp, that) - } - - /** - * Squeeze returns the intersection of the ranges this interval and that Interval - * Ignores binary point of argument - * Treat as an unsafe cast; gives undefined behavior if this signal's value is outside of the resulting range - * Adds no additional hardware; this strictly an unsafe type conversion to use at your own risk - * @param that - * @return - */ - final def squeeze(that: Interval): Interval = macro SourceInfoTransform.thatArg - def do_squeeze(that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { - val other = that - requireIsHardware(this, s"'this' ($this)") - requireIsHardware(other, s"'other' ($other)") - pushOp(DefPrim(sourceInfo, Interval(this.range.squeeze(that.range)), SqueezeOp, this.ref, other.ref)) - } - - /** - * Squeeze returns the intersection of the ranges this interval and that UInt - * Currently, that must have a defined width - * Treat as an unsafe cast; gives undefined behavior if this signal's value is outside of the resulting range - * Adds no additional hardware; this strictly an unsafe type conversion to use at your own risk - * @param that an UInt whose properties determine the squeezing - * @return - */ - final def squeeze(that: UInt): Interval = macro SourceInfoTransform.thatArg - def do_squeeze(that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { - that.widthOption match { - case Some(w) => - do_squeeze(Wire(Interval(IntervalRange(that.width, BinaryPoint(0))))) - case _ => - throwException(s"$this.squeeze($that) requires an UInt argument with a known width") - } - } - - /** - * Squeeze returns the intersection of the ranges this interval and that SInt - * Currently, that must have a defined width - * Treat as an unsafe cast; gives undefined behavior if this signal's value is outside of the resulting range - * Adds no additional hardware; this strictly an unsafe type conversion to use at your own risk - * @param that an SInt whose properties determine the squeezing - * @return - */ - final def squeeze(that: SInt): Interval = macro SourceInfoTransform.thatArg - def do_squeeze(that: SInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { - that.widthOption match { - case Some(w) => - do_squeeze(Wire(Interval(IntervalRange(that.width, BinaryPoint(0))))) - case _ => - throwException(s"$this.squeeze($that) requires an SInt argument with a known width") - } - } - - /** - * Squeeze returns the intersection of the ranges this interval and that IntervalRange - * Ignores binary point of argument - * Treat as an unsafe cast; gives undefined behavior if this signal's value is outside of the resulting range - * Adds no additional hardware; this strictly an unsafe type conversion to use at your own risk - * @param that an Interval whose properties determine the squeezing - * @return - */ - final def squeeze(that: IntervalRange): Interval = macro SourceInfoTransform.thatArg - def do_squeeze(that: IntervalRange)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { - val intervalLitOpt = Interval.getSmallestLegalLit(that) - val intervalLit = intervalLitOpt.getOrElse( - throwException(s"$this.squeeze($that) requires an Interval range with known lower and upper bounds") - ) - do_squeeze(intervalLit) - } - - - /** - * Wrap the value of this [[Interval]] into the range of a different Interval with a presumably smaller range. - * Ignores binary point of argument - * Errors if requires wrapping more than once - * @param that - * @return - */ - final def wrap(that: Interval): Interval = macro SourceInfoTransform.thatArg - - def do_wrap(that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { - val other = that - requireIsHardware(this, s"'this' ($this)") - requireIsHardware(other, s"'other' ($other)") - pushOp(DefPrim(sourceInfo, Interval(this.range.wrap(that.range)), WrapOp, this.ref, other.ref)) - } - - /** - * Wrap this interval into the range determined by that UInt - * Errors if requires wrapping more than once - * @param that an UInt whose properties determine the wrap - * @return - */ - final def wrap(that: UInt): Interval = macro SourceInfoTransform.thatArg - def do_wrap(that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { - that.widthOption match { - case Some(w) => - val u = BigDecimal(BigInt(1) << w) - 1 - do_wrap(0.U.asInterval(IntervalRange(firrtlir.Closed(0), firrtlir.Closed(u), BinaryPoint(0)))) - case _ => - throwException(s"$this.wrap($that) requires UInt with known width") - } - } - - /** - * Wrap this interval into the range determined by an SInt - * Errors if requires wrapping more than once - * @param that an SInt whose properties determine the bounds of the wrap - * @return - */ - final def wrap(that: SInt): Interval = macro SourceInfoTransform.thatArg - def do_wrap(that: SInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { - that.widthOption match { - case Some(w) => - val l = -BigDecimal(BigInt(1) << (that.getWidth - 1)) - val u = BigDecimal(BigInt(1) << (that.getWidth - 1)) - 1 - do_wrap(Wire(Interval(IntervalRange(firrtlir.Closed(l), firrtlir.Closed(u), BinaryPoint(0))))) - case _ => - throwException(s"$this.wrap($that) requires SInt with known width") - } - } - - /** - * Wrap this interval into the range determined by an IntervalRange - * Adds hardware to change values outside of wrapped range to be at the boundary - * Errors if requires wrapping more than once - * Ignores binary point of argument - * @param that an Interval whose properties determine the bounds of the wrap - * @return - */ - final def wrap(that: IntervalRange): Interval = macro SourceInfoTransform.thatArg - def do_wrap(that: IntervalRange)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { - (that.lowerBound, that.upperBound) match { - case (lower: firrtlconstraint.IsKnown, upperBound: firrtlconstraint.IsKnown) => - do_wrap(0.U.asInterval(IntervalRange(that.lowerBound, that.upperBound, BinaryPoint(0)))) - case _ => - throwException(s"$this.wrap($that) requires Interval argument with known lower and upper bounds") - } - } - - /** - * Clip this interval into the range determined by argument's range - * Adds hardware to change values outside of clipped range to be at the boundary - * Ignores binary point of argument - * @param that an Interval whose properties determine the clipping - * @return - */ - final def clip(that: Interval): Interval = macro SourceInfoTransform.thatArg - def do_clip(that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { - binop(sourceInfo, Interval(this.range.clip(that.range)), ClipOp, that) - } - - /** - * Clip this interval into the range determined by argument's range - * Adds hardware to change values outside of clipped range to be at the boundary - * @param that an UInt whose width determines the clipping - * @return - */ - final def clip(that: UInt): Interval = macro SourceInfoTransform.thatArg - def do_clip(that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { - require(that.widthKnown, "UInt clip width must be known") - val u = BigDecimal(BigInt(1) << that.getWidth) - 1 - do_clip(Wire(Interval(IntervalRange(firrtlir.Closed(0), firrtlir.Closed(u), BinaryPoint(0))))) - } - - /** - * Clip this interval into the range determined by argument's range - * Adds hardware to move values outside of clipped range to the boundary - * @param that an SInt whose width determines the clipping - * @return - */ - final def clip(that: SInt): Interval = macro SourceInfoTransform.thatArg - def do_clip(that: SInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { - require(that.widthKnown, "SInt clip width must be known") - val l = -BigDecimal(BigInt(1) << (that.getWidth - 1)) - val u = BigDecimal(BigInt(1) << (that.getWidth - 1)) - 1 - do_clip(Wire(Interval(IntervalRange(firrtlir.Closed(l), firrtlir.Closed(u), BinaryPoint(0))))) - } - - /** - * Clip this interval into the range determined by argument's range - * Adds hardware to move values outside of clipped range to the boundary - * Ignores binary point of argument - * @param that an SInt whose width determines the clipping - * @return - */ - final def clip(that: IntervalRange): Interval = macro SourceInfoTransform.thatArg - def do_clip(that: IntervalRange)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { - (that.lowerBound, that.upperBound) match { - case (lower: firrtlconstraint.IsKnown, upperBound: firrtlconstraint.IsKnown) => - do_clip(0.U.asInterval(IntervalRange(that.lowerBound, that.upperBound, BinaryPoint(0)))) - case _ => - throwException(s"$this.clip($that) requires Interval argument with known lower and upper bounds") - } - } - - override def do_asUInt(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = { - pushOp(DefPrim(sourceInfo, UInt(this.width), AsUIntOp, ref)) - } - override def do_asSInt(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = { - pushOp(DefPrim(sourceInfo, SInt(this.width), AsSIntOp, ref)) - } - - override def do_asFixedPoint(binaryPoint: BinaryPoint)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = { - binaryPoint match { - case KnownBinaryPoint(value) => - val iLit = ILit(value) - pushOp(DefPrim(sourceInfo, FixedPoint(width, binaryPoint), AsFixedPointOp, ref, iLit)) - case _ => - throwException( - s"cannot call $this.asFixedPoint(binaryPoint=$binaryPoint), you must specify a known binaryPoint") - } - } - - // TODO: intervals chick INVALID -- not enough args - def do_asInterval(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { - pushOp(DefPrim(sourceInfo, Interval(this.range), AsIntervalOp, ref)) - throwException(s"($this).asInterval must specify arguments INVALID") - } - - // TODO:(chick) intervals chick looks like this is wrong and only for FP? - def do_fromBits(that: Bits)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): this.type = { - /*val res = Wire(this, null).asInstanceOf[this.type] - res := (that match { - case fp: FixedPoint => fp.asSInt.asFixedPoint(this.binaryPoint) - case _ => that.asFixedPoint(this.binaryPoint) - }) - res*/ - throwException("fromBits INVALID for intervals") - } - - private[chisel3] override def connectFromBits(that: Bits) - (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions) { - this := that.asInterval(this.range) - } - } - - /** Use PrivateObject to force users to specify width and binaryPoint by name - */ - - /** - * Factory and convenience methods for the Interval class - * IMPORTANT: The API provided here is experimental and may change in the future. - */ - object Interval extends NumObject { - /** Create an Interval type with inferred width and binary point. */ - def apply(): Interval = Interval(range"[?,?]") - - /** Create an Interval type with specified width. */ - def apply(binaryPoint: BinaryPoint): Interval = { - val binaryPointString = binaryPoint match { - case KnownBinaryPoint(value) => s"$value" - case _ => s"" - } - Interval(range"[?,?].$binaryPointString") - } - - /** Create an Interval type with specified width. */ - def apply(width: Width): Interval = Interval(width, 0.BP) - - /** Create an Interval type with specified width and binary point */ - def apply(width: Width, binaryPoint: BinaryPoint): Interval = { - Interval(IntervalRange(width, binaryPoint)) - } - - /** Create an Interval type with specified range. - * @param range defines the properties - */ - def apply(range: IntervalRange): Interval = { - new Interval(range) - } - - /** Creates a Interval connected to a Interval literal with the value zero */ - def Zero: Interval = Lit(0, 1.W, 0.BP) - - /** Creates an Interval zero that supports the given range - * Useful for creating a Interval register that has a desired number of bits - * {{{ - * val myRegister = RegInit(Interval.Zero(r"[0,12]") - * }}} - * @param range - * @return - */ - def Zero(range: IntervalRange): Interval = Lit(0, range) - - /** Make an interval from this BigInt, the BigInt is treated as bits - * So lower binaryPoint number of bits will treated as mantissa - * - * @param value - * @param width - * @param binaryPoint - * @return - */ - def fromBigInt(value: BigInt, width: Width = Width(), binaryPoint: BinaryPoint = 0.BP): Interval = { - Interval.Lit(value, Width(), binaryPoint) - } - - /** Create an Interval literal with inferred width from Double. - * Use PrivateObject to force users to specify width and binaryPoint by name - */ - def fromDouble(value: Double, dummy: PrivateType = PrivateObject, - width: Width, binaryPoint: BinaryPoint): Interval = { - fromBigInt( - toBigInt(value, binaryPoint), width = width, binaryPoint = binaryPoint - ) - } - - /** Create an Interval literal with inferred width from Double. - * Use PrivateObject to force users to specify width and binaryPoint by name - */ - def fromBigDecimal(value: Double, dummy: PrivateType = PrivateObject, - width: Width, binaryPoint: BinaryPoint): Interval = { - fromBigInt( - toBigInt(value, binaryPoint), width = width, binaryPoint = binaryPoint - ) - } - - protected[chisel3] def Lit(value: BigInt, width: Width, binaryPoint: BinaryPoint): Interval = { - width match { - case KnownWidth(w) => - if(value >= 0 && value.bitLength >= w || value < 0 && value.bitLength > w) { - throw new ChiselException( - s"Error literal interval value $value is too many bits for specified width $w" - ) - } - case _ => - } - val lit = IntervalLit(value, width, binaryPoint) - val bound = firrtlir.Closed(Interval.toBigDecimal(value, binaryPoint.asInstanceOf[KnownBinaryPoint].value)) - val result = new Interval(IntervalRange(bound, bound, binaryPoint)) - lit.bindLitArg(result) - } - - protected[chisel3] def Lit(value: BigInt, range: IntervalRange): Interval = { - val lit = IntervalLit(value, range.getWidth, range.binaryPoint) - val bigDecimal = BigDecimal(value) / (1 << lit.binaryPoint.get) - val inRange = (range.lowerBound, range.upperBound) match { - case (firrtlir.Closed(l), firrtlir.Closed(u)) => l <= bigDecimal && bigDecimal <= u - case (firrtlir.Closed(l), firrtlir.Open(u)) => l <= bigDecimal && bigDecimal < u - case (firrtlir.Open(l), firrtlir.Closed(u)) => l < bigDecimal && bigDecimal <= u - case (firrtlir.Open(l), firrtlir.Open(u)) => l < bigDecimal && bigDecimal < u - } - if(! inRange) { - throw new ChiselException( - s"Error literal interval value $bigDecimal is not contained in specified range $range" - ) - } - val result = Interval(range) - lit.bindLitArg(result) - } - - /** - * This returns the smallest Interval literal that can legally fit in range, if possible - * If the lower bound or binary point is not known then return None - * - * @param range use to figure low number - * @return - */ - def getSmallestLegalLit(range: IntervalRange): Option[Interval] = { - val bp = range.binaryPoint - range.lowerBound match { - case firrtlir.Closed(lowerBound) => - Some(Interval.Lit(toBigInt(lowerBound.toDouble, bp), width = range.getWidth, bp)) - case firrtlir.Open(lowerBound) => - Some(Interval.Lit(toBigInt(lowerBound.toDouble, bp) + BigInt(1), width = range.getWidth, bp)) - case _ => - None - } - } - - /** - * This returns the largest Interval literal that can legally fit in range, if possible - * If the upper bound or binary point is not known then return None - * - * @param range use to figure low number - * @return - */ - def getLargestLegalLit(range: IntervalRange): Option[Interval] = { - val bp = range.binaryPoint - range.upperBound match { - case firrtlir.Closed(upperBound) => - Some(Interval.Lit(toBigInt(upperBound.toDouble, bp), width = range.getWidth, bp)) - case firrtlir.Open(upperBound) => - Some(Interval.Lit(toBigInt(upperBound.toDouble, bp) - BigInt(1), width = range.getWidth, bp)) - case _ => - None - } - } - - /** Contains the implicit classes used to provide the .I methods to create intervals - * from the standard numberic types. - * {{{ - * val x = 7.I - * val y = 7.5.I(4.BP) - * }}} - */ - object Implicits { - implicit class fromBigIntToLiteralInterval(bigInt: BigInt) { - def I: Interval = { - Interval.Lit(bigInt, width = Width(), 0.BP) - } - - def I(binaryPoint: BinaryPoint): Interval = { - Interval.Lit(bigInt, width = Width(), binaryPoint = binaryPoint) - } - - def I(width: Width, binaryPoint: BinaryPoint): Interval = { - Interval.Lit(bigInt, width, binaryPoint) - } - - def I(range: IntervalRange): Interval = { - Interval.Lit(bigInt, range) - } - } - - implicit class fromIntToLiteralInterval(int: Int) extends fromBigIntToLiteralInterval(int) - implicit class fromLongToLiteralInterval(long: Long) extends fromBigIntToLiteralInterval(long) - - implicit class fromBigDecimalToLiteralInterval(bigDecimal: BigDecimal) { - def I: Interval = { - Interval.Lit(Interval.toBigInt(bigDecimal, 0.BP), width = Width(), 0.BP) - } - - def I(binaryPoint: BinaryPoint): Interval = { - Interval.Lit(Interval.toBigInt(bigDecimal, binaryPoint), width = Width(), binaryPoint = binaryPoint) - } - - def I(width: Width, binaryPoint: BinaryPoint): Interval = { - Interval.Lit(Interval.toBigInt(bigDecimal, binaryPoint), width, binaryPoint) - } - - def I(range: IntervalRange): Interval = { - Interval.Lit(Interval.toBigInt(bigDecimal, range.binaryPoint), range) - } - } - - implicit class fromDoubleToLiteralInterval(double: Double) - extends fromBigDecimalToLiteralInterval(BigDecimal(double)) - } - } -} - - diff --git a/chiselFrontend/src/main/scala/chisel3/BlackBox.scala b/chiselFrontend/src/main/scala/chisel3/BlackBox.scala deleted file mode 100644 index f29962d7..00000000 --- a/chiselFrontend/src/main/scala/chisel3/BlackBox.scala +++ /dev/null @@ -1,181 +0,0 @@ -// See LICENSE for license details. - -package chisel3 - -import chisel3.experimental.{BaseModule, Param} -import chisel3.internal.BaseBlackBox -import chisel3.internal.Builder.pushCommand -import chisel3.internal.firrtl._ -import chisel3.internal.throwException -import chisel3.internal.sourceinfo.{SourceInfo, UnlocatableSourceInfo} - -package internal { - - private[chisel3] abstract class BaseBlackBox extends BaseModule - -} - -package experimental { - - /** Parameters for BlackBoxes */ - sealed abstract class Param - case class IntParam(value: BigInt) extends Param - case class DoubleParam(value: Double) extends Param - case class StringParam(value: String) extends Param - /** Unquoted String */ - case class RawParam(value: String) extends Param - - /** Defines a black box, which is a module that can be referenced from within - * Chisel, but is not defined in the emitted Verilog. Useful for connecting - * to RTL modules defined outside Chisel. - * - * A variant of BlackBox, this has a more consistent naming scheme in allowing - * multiple top-level IO and does not drop the top prefix. - * - * @example - * Some design require a differential input clock to clock the all design. - * With the xilinx FPGA for example, a Verilog template named IBUFDS must be - * integrated to use differential input: - * {{{ - * IBUFDS #(.DIFF_TERM("TRUE"), - * .IOSTANDARD("DEFAULT")) ibufds ( - * .IB(ibufds_IB), - * .I(ibufds_I), - * .O(ibufds_O) - * ); - * }}} - * - * To instantiate it, a BlackBox can be used like following: - * {{{ - * import chisel3._ - * import chisel3.experimental._ - * - * // Example with Xilinx differential buffer IBUFDS - * class IBUFDS extends ExtModule(Map("DIFF_TERM" -> "TRUE", // Verilog parameters - * "IOSTANDARD" -> "DEFAULT" - * )) { - * val O = IO(Output(Clock())) - * val I = IO(Input(Clock())) - * val IB = IO(Input(Clock())) - * } - * }}} - * @note The parameters API is experimental and may change - */ - abstract class ExtModule(val params: Map[String, Param] = Map.empty[String, Param]) extends BaseBlackBox { - private[chisel3] override def generateComponent(): Component = { - require(!_closed, "Can't generate module more than once") - _closed = true - - val names = nameIds(classOf[ExtModule]) - - // Name ports based on reflection - for (port <- getModulePorts) { - require(names.contains(port), s"Unable to name port $port in $this") - port.setRef(ModuleIO(this, _namespace.name(names(port)))) - } - - // All suggestions are in, force names to every node. - // While BlackBoxes are not supposed to have an implementation, we still need to call - // _onModuleClose on all nodes (for example, Aggregates use it for recursive naming). - for (id <- getIds) { - id._onModuleClose - } - - val firrtlPorts = getModulePorts map {port => Port(port, port.specifiedDirection)} - val component = DefBlackBox(this, name, firrtlPorts, SpecifiedDirection.Unspecified, params) - _component = Some(component) - component - } - - private[chisel3] def initializeInParent(parentCompileOptions: CompileOptions): Unit = { - implicit val sourceInfo = UnlocatableSourceInfo - - for (x <- getModulePorts) { - pushCommand(DefInvalid(sourceInfo, x.ref)) - } - } - } -} - -/** Defines a black box, which is a module that can be referenced from within - * Chisel, but is not defined in the emitted Verilog. Useful for connecting - * to RTL modules defined outside Chisel. - * - * @example - * Some design require a differential input clock to clock the all design. - * With the xilinx FPGA for example, a Verilog template named IBUFDS must be - * integrated to use differential input: - * {{{ - * IBUFDS #(.DIFF_TERM("TRUE"), - * .IOSTANDARD("DEFAULT")) ibufds ( - * .IB(ibufds_IB), - * .I(ibufds_I), - * .O(ibufds_O) - * ); - * }}} - * - * To instantiate it, a BlackBox can be used like following: - * {{{ - * import chisel3._ - * import chisel3.experimental._ - * - * // Example with Xilinx differential buffer IBUFDS - * class IBUFDS extends BlackBox(Map("DIFF_TERM" -> "TRUE", // Verilog parameters - * "IOSTANDARD" -> "DEFAULT" - * )) { - * val io = IO(new Bundle { - * val O = Output(Clock()) // IO names will be the same - * val I = Input(Clock()) // (without 'io_' in prefix) - * val IB = Input(Clock()) // - * }) - * } - * }}} - * @note The parameters API is experimental and may change - */ -abstract class BlackBox(val params: Map[String, Param] = Map.empty[String, Param])(implicit compileOptions: CompileOptions) extends BaseBlackBox { // scalastyle:ignore line.size.limit - def io: Record - - // Allow access to bindings from the compatibility package - protected def _compatIoPortBound() = portsContains(io) // scalastyle:ignore method.name - - private[chisel3] override def generateComponent(): Component = { - _compatAutoWrapPorts() // pre-IO(...) compatibility hack - - // Restrict IO to just io, clock, and reset - require(io != null, "BlackBox must have io") - require(portsContains(io), "BlackBox must have io wrapped in IO(...)") - require(portsSize == 1, "BlackBox must only have io as IO") - - require(!_closed, "Can't generate module more than once") - _closed = true - - val namedPorts = io.elements.toSeq.reverse // ListMaps are stored in reverse order - - // setRef is not called on the actual io. - // There is a risk of user improperly attempting to connect directly with io - // Long term solution will be to define BlackBox IO differently as part of - // it not descending from the (current) Module - for ((name, port) <- namedPorts) { - port.setRef(ModuleIO(this, _namespace.name(name))) - } - - // We need to call forceName and onModuleClose on all of the sub-elements - // of the io bundle, but NOT on the io bundle itself. - // Doing so would cause the wrong names to be assigned, since their parent - // is now the module itself instead of the io bundle. - for (id <- getIds; if id ne io) { - id._onModuleClose - } - - val firrtlPorts = namedPorts map {namedPort => Port(namedPort._2, namedPort._2.specifiedDirection)} - val component = DefBlackBox(this, name, firrtlPorts, io.specifiedDirection, params) - _component = Some(component) - component - } - - private[chisel3] def initializeInParent(parentCompileOptions: CompileOptions): Unit = { - for ((_, port) <- io.elements) { - pushCommand(DefInvalid(UnlocatableSourceInfo, port.ref)) - } - } -} diff --git a/chiselFrontend/src/main/scala/chisel3/BoolFactory.scala b/chiselFrontend/src/main/scala/chisel3/BoolFactory.scala deleted file mode 100644 index bccd6414..00000000 --- a/chiselFrontend/src/main/scala/chisel3/BoolFactory.scala +++ /dev/null @@ -1,22 +0,0 @@ -// See LICENSE for license details. - -package chisel3 - -import chisel3.internal.firrtl.{ULit, Width} - -// scalastyle:off method.name - -trait BoolFactory { - /** Creates an empty Bool. - */ - def apply(): Bool = new Bool() - - /** Creates Bool literal. - */ - protected[chisel3] def Lit(x: Boolean): Bool = { - val result = new Bool() - val lit = ULit(if (x) 1 else 0, Width(1)) - // Ensure we have something capable of generating a name. - lit.bindLitArg(result) - } -} diff --git a/chiselFrontend/src/main/scala/chisel3/Clock.scala b/chiselFrontend/src/main/scala/chisel3/Clock.scala deleted file mode 100644 index d7975b1e..00000000 --- a/chiselFrontend/src/main/scala/chisel3/Clock.scala +++ /dev/null @@ -1,43 +0,0 @@ -// See LICENSE for license details. - -package chisel3 - -import scala.language.experimental.macros -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(private[chisel3] val width: Width = Width(1)) extends Element { - override def toString: String = s"Clock$bindingToString" - - def cloneType: this.type = Clock().asInstanceOf[this.type] - - private[chisel3] def typeEquivalent(that: Data): Boolean = - this.getClass == that.getClass - - override def connect(that: Data)(implicit sourceInfo: SourceInfo, connectCompileOptions: CompileOptions): Unit = that match { // scalastyle:ignore line.size.limit - case _: Clock => super.connect(that)(sourceInfo, connectCompileOptions) - case _ => super.badConnect(that)(sourceInfo) - } - - override def litOption: Option[BigInt] = None - - /** Not really supported */ - def toPrintable: Printable = PString("CLOCK") - - /** Returns the contents of the clock wire as a [[Bool]]. */ - final def asBool(): Bool = macro SourceInfoTransform.noArg - def do_asBool(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = this.asUInt().asBool() - - override def do_asUInt(implicit sourceInfo: SourceInfo, connectCompileOptions: CompileOptions): UInt = pushOp(DefPrim(sourceInfo, UInt(this.width), AsUIntOp, ref)) // scalastyle:ignore line.size.limit - private[chisel3] override def connectFromBits(that: Bits)(implicit sourceInfo: SourceInfo, - compileOptions: CompileOptions): Unit = { - this := that.asBool.asClock - } -} diff --git a/chiselFrontend/src/main/scala/chisel3/CompileOptions.scala b/chiselFrontend/src/main/scala/chisel3/CompileOptions.scala deleted file mode 100644 index ed410c6e..00000000 --- a/chiselFrontend/src/main/scala/chisel3/CompileOptions.scala +++ /dev/null @@ -1,80 +0,0 @@ -// See LICENSE for license details. - -package chisel3 - -import scala.language.experimental.macros -import scala.reflect.macros.blackbox.Context - -trait CompileOptions { - // Should Record connections require a strict match of fields. - // If true and the same fields aren't present in both source and sink, a MissingFieldException, - // MissingLeftFieldException, or MissingRightFieldException will be thrown. - val connectFieldsMustMatch: Boolean - // When creating an object that takes a type argument, the argument must be unbound (a pure type). - val declaredTypeMustBeUnbound: Boolean - // If a connection operator fails, don't try the connection with the operands (source and sink) reversed. - val dontTryConnectionsSwapped: Boolean - // If connection directionality is not explicit, do not use heuristics to attempt to determine it. - val dontAssumeDirectionality: Boolean - // Check that referenced Data have actually been declared. - val checkSynthesizable: Boolean - // Require explicit assignment of DontCare to generate "x is invalid" - val explicitInvalidate: Boolean - // Should the reset type of Module be a Bool or a Reset - val inferModuleReset: Boolean -} - -object CompileOptions { - // Provides a low priority Strict default. Can be overridden by importing the NotStrict option. - // Implemented as a macro to prevent this from being used inside chisel core. - implicit def materialize: CompileOptions = macro materialize_impl - - def materialize_impl(c: Context): c.Tree = { - import c.universe._ - q"_root_.chisel3.ExplicitCompileOptions.Strict" - } -} - -object ExplicitCompileOptions { - case class CompileOptionsClass ( - // Should Record connections require a strict match of fields. - // If true and the same fields aren't present in both source and sink, a MissingFieldException, - // MissingLeftFieldException, or MissingRightFieldException will be thrown. - val connectFieldsMustMatch: Boolean, - // When creating an object that takes a type argument, the argument must be unbound (a pure type). - val declaredTypeMustBeUnbound: Boolean, - // If a connection operator fails, don't try the connection with the operands (source and sink) reversed. - val dontTryConnectionsSwapped: Boolean, - // If connection directionality is not explicit, do not use heuristics to attempt to determine it. - val dontAssumeDirectionality: Boolean, - // Check that referenced Data have actually been declared. - val checkSynthesizable: Boolean, - // Require an explicit DontCare assignment to generate a firrtl DefInvalid - val explicitInvalidate: Boolean, - // Should the reset type of Module be a Bool or a Reset - val inferModuleReset: Boolean - ) extends CompileOptions - - // Collection of "not strict" connection compile options. - // These provide compatibility with existing code. - implicit val NotStrict = new CompileOptionsClass ( - connectFieldsMustMatch = false, - declaredTypeMustBeUnbound = false, - dontTryConnectionsSwapped = false, - dontAssumeDirectionality = false, - checkSynthesizable = false, - explicitInvalidate = false, - inferModuleReset = false - ) - - // Collection of "strict" connection compile options, preferred for new code. - implicit val Strict = new CompileOptionsClass ( - connectFieldsMustMatch = true, - declaredTypeMustBeUnbound = true, - dontTryConnectionsSwapped = true, - dontAssumeDirectionality = true, - checkSynthesizable = true, - explicitInvalidate = true, - inferModuleReset = true - ) -} diff --git a/chiselFrontend/src/main/scala/chisel3/Data.scala b/chiselFrontend/src/main/scala/chisel3/Data.scala deleted file mode 100644 index 6574a39d..00000000 --- a/chiselFrontend/src/main/scala/chisel3/Data.scala +++ /dev/null @@ -1,729 +0,0 @@ -// See LICENSE for license details. - -package chisel3 - -import scala.language.experimental.macros -import chisel3.experimental.{Analog, DataMirror, FixedPoint, Interval} -import chisel3.internal.Builder.pushCommand -import chisel3.internal._ -import chisel3.internal.firrtl._ -import chisel3.internal.sourceinfo.{DeprecatedSourceInfo, SourceInfo, SourceInfoTransform, UnlocatableSourceInfo} - -/** User-specified directions. - */ -sealed abstract class SpecifiedDirection -object SpecifiedDirection { - /** Default user direction, also meaning 'not-flipped' - */ - case object Unspecified extends SpecifiedDirection - /** Node and its children are forced as output - */ - case object Output extends SpecifiedDirection - /** Node and its children are forced as inputs - */ - case object Input extends SpecifiedDirection - /** Mainly for containers, children are flipped. - */ - case object Flip extends SpecifiedDirection - - def flip(dir: SpecifiedDirection): SpecifiedDirection = dir match { - case Unspecified => Flip - case Flip => Unspecified - case Output => Input - case Input => Output - } - - /** Returns the effective SpecifiedDirection of this node given the parent's effective SpecifiedDirection - * and the user-specified SpecifiedDirection of this node. - */ - def fromParent(parentDirection: SpecifiedDirection, thisDirection: SpecifiedDirection): SpecifiedDirection = - (parentDirection, thisDirection) match { - case (SpecifiedDirection.Output, _) => SpecifiedDirection.Output - case (SpecifiedDirection.Input, _) => SpecifiedDirection.Input - case (SpecifiedDirection.Unspecified, thisDirection) => thisDirection - case (SpecifiedDirection.Flip, thisDirection) => SpecifiedDirection.flip(thisDirection) - } - - private[chisel3] def specifiedDirection[T<:Data](source: T)(dir: SpecifiedDirection)(implicit compileOptions: CompileOptions): T = { - if (compileOptions.checkSynthesizable) { - requireIsChiselType(source) - } - val out = source.cloneType.asInstanceOf[T] - out.specifiedDirection = dir - out - } - -} - -/** 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 { - /** The object does not exist / is empty and hence has no direction - */ - case object Empty extends 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 - - def fromSpecified(direction: SpecifiedDirection): ActualDirection = direction match { - case SpecifiedDirection.Unspecified | SpecifiedDirection.Flip => ActualDirection.Unspecified - case SpecifiedDirection.Output => ActualDirection.Output - case SpecifiedDirection.Input => ActualDirection.Input - } - - /** Determine the actual binding of a container given directions of its children. - * Returns None in the case of mixed specified / unspecified directionality. - */ - def fromChildren(childDirections: Set[ActualDirection], containerDirection: SpecifiedDirection): - Option[ActualDirection] = { - if (childDirections == Set()) { // Sadly, Scala can't do set matching - ActualDirection.fromSpecified(containerDirection) match { - case ActualDirection.Unspecified => Some(ActualDirection.Empty) // empty direction if relative / no direction - case dir => Some(dir) // use assigned direction if specified - } - } else if (childDirections == Set(ActualDirection.Unspecified)) { - Some(ActualDirection.Unspecified) - } else if (childDirections == Set(ActualDirection.Input)) { - Some(ActualDirection.Input) - } else if (childDirections == Set(ActualDirection.Output)) { - Some(ActualDirection.Output) - } else if (childDirections subsetOf - Set(ActualDirection.Output, ActualDirection.Input, - ActualDirection.Bidirectional(ActualDirection.Default), - ActualDirection.Bidirectional(ActualDirection.Flipped))) { - containerDirection match { - case SpecifiedDirection.Unspecified => Some(ActualDirection.Bidirectional(ActualDirection.Default)) - case SpecifiedDirection.Flip => Some(ActualDirection.Bidirectional(ActualDirection.Flipped)) - case _ => throw new RuntimeException("Unexpected forced Input / Output") - } - } else { - None - } - } -} - -package experimental { - - /** Experimental hardware construction reflection API - */ - object DataMirror { - def widthOf(target: Data): Width = target.width - def specifiedDirectionOf(target: Data): SpecifiedDirection = target.specifiedDirection - def directionOf(target: Data): ActualDirection = { - requireIsHardware(target, "node requested directionality on") - target.direction - } - - // Returns the top-level module ports - // TODO: maybe move to something like Driver or DriverUtils, since this is mainly for interacting - // with compiled artifacts (vs. elaboration-time reflection)? - def modulePorts(target: BaseModule): Seq[(String, Data)] = target.getChiselPorts - - // Returns all module ports with underscore-qualified names - def fullModulePorts(target: BaseModule): Seq[(String, Data)] = { - def getPortNames(name: String, data: Data): Seq[(String, Data)] = Seq(name -> data) ++ (data match { - case _: Element => Seq() - case r: Record => r.elements.toSeq flatMap { case (eltName, elt) => getPortNames(s"${name}_${eltName}", elt) } - case v: Vec[_] => v.zipWithIndex flatMap { case (elt, index) => getPortNames(s"${name}_${index}", elt) } - }) - modulePorts(target).flatMap { case (name, data) => - getPortNames(name, data).toList - } - } - - // Internal reflection-style APIs, subject to change and removal whenever. - object internal { // scalastyle:ignore object.name - def isSynthesizable(target: Data): Boolean = target.isSynthesizable - // For those odd cases where you need to care about object reference and uniqueness - def chiselTypeClone[T<:Data](target: Data): T = { - target.cloneTypeFull.asInstanceOf[T] - } - } - } -} - -/** Creates a clone of the super-type of the input elements. Super-type is defined as: - * - for Bits type of the same class: the cloned type of the largest width - * - Bools are treated as UInts - * - For other types of the same class are are the same: clone of any of the elements - * - Otherwise: fail - */ -//scalastyle:off cyclomatic.complexity -private[chisel3] object cloneSupertype { - def apply[T <: Data](elts: Seq[T], createdType: String)(implicit sourceInfo: SourceInfo, - compileOptions: CompileOptions): T = { - require(!elts.isEmpty, s"can't create $createdType with no inputs") - - val filteredElts = elts.filter(_ != DontCare) - require(!filteredElts.isEmpty, s"can't create $createdType with only DontCare inputs") - - if (filteredElts.head.isInstanceOf[Bits]) { - val model: T = filteredElts reduce { (elt1: T, elt2: T) => ((elt1, elt2) match { - case (elt1: Bool, elt2: Bool) => elt1 - case (elt1: Bool, elt2: UInt) => elt2 // TODO: what happens with zero width UInts? - case (elt1: UInt, elt2: Bool) => elt1 // TODO: what happens with zero width UInts? - case (elt1: UInt, elt2: UInt) => - // TODO: perhaps redefine Widths to allow >= op? - if (elt1.width == (elt1.width max elt2.width)) elt1 else elt2 - case (elt1: SInt, elt2: SInt) => if (elt1.width == (elt1.width max elt2.width)) elt1 else elt2 - case (elt1: FixedPoint, elt2: FixedPoint) => { - (elt1.binaryPoint, elt2.binaryPoint, elt1.width, elt2.width) match { - case (KnownBinaryPoint(bp1), KnownBinaryPoint(bp2), KnownWidth(w1), KnownWidth(w2)) => - val maxBinaryPoint = bp1 max bp2 - val maxIntegerWidth = (w1 - bp1) max (w2 - bp2) - FixedPoint((maxIntegerWidth + maxBinaryPoint).W, (maxBinaryPoint).BP) - case (KnownBinaryPoint(bp1), KnownBinaryPoint(bp2), _, _) => - FixedPoint(Width(), (bp1 max bp2).BP) - case _ => FixedPoint() - } - } - case (elt1: Interval, elt2: Interval) => - val range = if(elt1.range.width == elt1.range.width.max(elt2.range.width)) elt1.range else elt2.range - Interval(range) - case (elt1, elt2) => - throw new AssertionError( - s"can't create $createdType with heterogeneous types ${elt1.getClass} and ${elt2.getClass}") - }).asInstanceOf[T] } - model.cloneTypeFull - } - else { - for (elt <- filteredElts.tail) { - require(elt.getClass == filteredElts.head.getClass, - s"can't create $createdType with heterogeneous types ${filteredElts.head.getClass} and ${elt.getClass}") - require(elt typeEquivalent filteredElts.head, - s"can't create $createdType with non-equivalent types ${filteredElts.head} and ${elt}") - } - filteredElts.head.cloneTypeFull - } - } -} - -/** Returns the chisel type of a hardware object, allowing other hardware to be constructed from it. - */ -object chiselTypeOf { - def apply[T <: Data](target: T): T = { - requireIsHardware(target) - target.cloneTypeFull.asInstanceOf[T] - } -} - -/** -* Input, Output, and Flipped are used to define the directions of Module IOs. -* -* Note that they currently clone their source argument, including its bindings. -* -* 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 = { - SpecifiedDirection.specifiedDirection(source)(SpecifiedDirection.Input) - } -} -object Output { - def apply[T<:Data](source: T)(implicit compileOptions: CompileOptions): T = { - SpecifiedDirection.specifiedDirection(source)(SpecifiedDirection.Output) - } -} - -object Flipped { - def apply[T<:Data](source: T)(implicit compileOptions: CompileOptions): T = { - SpecifiedDirection.specifiedDirection(source)(SpecifiedDirection.flip(source.specifiedDirection)) - } -} - -/** This forms the root of the type system for wire data types. The data value - * must be representable as some number (need not be known at Chisel compile - * time) of bits, and must have methods to pack / unpack structured data to / - * from bits. - * - * @groupdesc Connect Utilities for connecting hardware components - * @define coll data - */ -abstract class Data extends HasId with NamedComponent with SourceInfoDoc { // scalastyle:ignore number.of.methods - // This is a bad API that punches through object boundaries. - @deprecated("pending removal once all instances replaced", "chisel3") - private[chisel3] def flatten: IndexedSeq[Element] = { - this match { - case elt: Aggregate => elt.getElements.toIndexedSeq flatMap {_.flatten} - case elt: Element => IndexedSeq(elt) - case elt => throwException(s"Cannot flatten type ${elt.getClass}") - } - } - - // User-specified direction, local at this node only. - // Note that the actual direction of this node can differ from child and parent specifiedDirection. - private var _specifiedDirection: SpecifiedDirection = SpecifiedDirection.Unspecified - private[chisel3] def specifiedDirection: SpecifiedDirection = _specifiedDirection - private[chisel3] def specifiedDirection_=(direction: SpecifiedDirection) = { - if (_specifiedDirection != SpecifiedDirection.Unspecified) { - this match { - // Anything flies in compatibility mode - case t: Record if !t.compileOptions.dontAssumeDirectionality => - case _ => throw RebindingException(s"Attempted reassignment of user-specified direction to $this") - } - } - _specifiedDirection = direction - } - - /** This overwrites a relative SpecifiedDirection with an explicit one, and is used to implement - * the compatibility layer where, at the elements, Flip is Input and unspecified is Output. - * DO NOT USE OUTSIDE THIS PURPOSE. THIS OPERATION IS DANGEROUS! - */ - private[chisel3] def _assignCompatibilityExplicitDirection: Unit = { // scalastyle:off method.name - (this, _specifiedDirection) match { - case (_: Analog, _) => // nothing to do - case (_, SpecifiedDirection.Unspecified) => _specifiedDirection = SpecifiedDirection.Output - case (_, SpecifiedDirection.Flip) => _specifiedDirection = SpecifiedDirection.Input - case (_, SpecifiedDirection.Input | SpecifiedDirection.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 - // Only valid after node is bound (synthesizable), crashes otherwise - protected[chisel3] def binding: Option[Binding] = _binding - protected def binding_=(target: Binding) { - if (_binding.isDefined) { - throw RebindingException(s"Attempted reassignment of binding to $this") - } - _binding = Some(target) - } - - // Similar to topBindingOpt except it explicitly excludes SampleElements which are bound but not - // hardware - private[chisel3] final def isSynthesizable: Boolean = _binding.map { - case ChildBinding(parent) => parent.isSynthesizable - case _: TopBinding => true - case _: SampleElementBinding[_] => false - }.getOrElse(false) - - private[chisel3] def topBindingOpt: Option[TopBinding] = _binding.flatMap { - case ChildBinding(parent) => parent.topBindingOpt - case bindingVal: TopBinding => Some(bindingVal) - case SampleElementBinding(parent) => parent.topBindingOpt - } - - private[chisel3] def topBinding: TopBinding = topBindingOpt.get - - /** 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: SpecifiedDirection = SpecifiedDirection.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[chisel3] def direction_=(actualDirection: ActualDirection) { - if (_direction.isDefined) { - throw RebindingException(s"Attempted reassignment of resolved direction to $this") - } - _direction = Some(actualDirection) - } - - // User-friendly representation of the binding as a helper function for toString. - // Provides a unhelpful fallback for literals, which should have custom rendering per - // Data-subtype. - // TODO Is this okay for sample_element? It *shouldn't* be visible to users - protected def bindingToString: String = topBindingOpt match { - case None => "" - case Some(OpBinding(enclosure)) => s"(OpResult in ${enclosure.desiredName})" - case Some(MemoryPortBinding(enclosure)) => s"(MemPort in ${enclosure.desiredName})" - case Some(PortBinding(enclosure)) if !enclosure.isClosed => s"(IO in unelaborated ${enclosure.desiredName})" - case Some(PortBinding(enclosure)) if enclosure.isClosed => - DataMirror.fullModulePorts(enclosure).find(_._2 eq this) match { - case Some((name, _)) => s"(IO $name in ${enclosure.desiredName})" - case None => s"(IO (unknown) in ${enclosure.desiredName})" - } - case Some(RegBinding(enclosure)) => s"(Reg in ${enclosure.desiredName})" - case Some(WireBinding(enclosure)) => s"(Wire in ${enclosure.desiredName})" - case Some(DontCareBinding()) => s"(DontCare)" - case Some(ElementLitBinding(litArg)) => s"(unhandled literal)" - case Some(BundleLitBinding(litMap)) => s"(unhandled bundle literal)" - } - - // 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 - private[chisel3] def allElements: Seq[Element] - - private[chisel3] def badConnect(that: Data)(implicit sourceInfo: SourceInfo): Unit = - throwException(s"cannot connect ${this} and ${that}") - private[chisel3] def connect(that: Data)(implicit sourceInfo: SourceInfo, connectCompileOptions: CompileOptions): Unit = { // scalastyle:ignore line.size.limit - if (connectCompileOptions.checkSynthesizable) { - requireIsHardware(this, "data to be connected") - requireIsHardware(that, "data to be connected") - this.topBinding match { - case _: ReadOnlyBinding => throwException(s"Cannot reassign to read-only $this") - case _ => // fine - } - try { - MonoConnect.connect(sourceInfo, connectCompileOptions, this, that, Builder.referenceUserModule) - } catch { - case MonoConnectException(message) => - throwException( - s"Connection between sink ($this) and source ($that) failed @$message" - ) - } - } else { - this legacyConnect that - } - } - private[chisel3] def bulkConnect(that: Data)(implicit sourceInfo: SourceInfo, connectCompileOptions: CompileOptions): Unit = { // scalastyle:ignore line.size.limit - if (connectCompileOptions.checkSynthesizable) { - requireIsHardware(this, s"data to be bulk-connected") - requireIsHardware(that, s"data to be bulk-connected") - (this.topBinding, that.topBinding) match { - case (_: ReadOnlyBinding, _: ReadOnlyBinding) => throwException(s"Both $this and $that are read-only") - // DontCare cannot be a sink (LHS) - case (_: DontCareBinding, _) => throw BiConnect.DontCareCantBeSink - case _ => // fine - } - try { - BiConnect.connect(sourceInfo, connectCompileOptions, this, that, Builder.referenceUserModule) - } catch { - case BiConnectException(message) => - throwException( - s"Connection between left ($this) and source ($that) failed @$message" - ) - } - } else { - this legacyConnect that - } - } - - /** Whether this Data has the same model ("data type") as that Data. - * Data subtypes should overload this with checks against their own type. - */ - private[chisel3] def typeEquivalent(that: Data): Boolean - - // Internal API: returns a ref that can be assigned to, if consistent with the binding - private[chisel3] def lref: Node = { - requireIsHardware(this) - topBindingOpt match { - case Some(binding: ReadOnlyBinding) => throwException(s"internal error: attempted to generate LHS ref to ReadOnlyBinding $binding") // scalastyle:ignore line.size.limit - case Some(binding: TopBinding) => Node(this) - case opt => throwException(s"internal error: unknown binding $opt in generating LHS ref") - } - } - - - // Internal API: returns a ref, if bound. Literals should override this as needed. - private[chisel3] def ref: Arg = { - requireIsHardware(this) - topBindingOpt match { - case Some(binding: LitBinding) => throwException(s"internal error: can't handle literal binding $binding") - case Some(binding: TopBinding) => Node(this) - case opt => throwException(s"internal error: unknown binding $opt in generating LHS ref") - } - } - - private[chisel3] def width: Width - private[chisel3] def legacyConnect(that: Data)(implicit sourceInfo: SourceInfo): Unit - - /** Internal API; Chisel users should look at chisel3.chiselTypeOf(...). - * - * cloneType must be defined for any Chisel object extending Data. - * It is responsible for constructing a basic copy of the object being cloned. - * - * @return a copy of the object. - */ - def cloneType: this.type - - /** Internal API; Chisel users should look at chisel3.chiselTypeOf(...). - * - * Returns a copy of this data type, with hardware bindings (if any) removed. - * Directionality data is still preserved. - */ - private[chisel3] def cloneTypeFull: this.type = { - val clone = this.cloneType.asInstanceOf[this.type] // get a fresh object, without bindings - // Only the top-level direction needs to be fixed up, cloneType should do the rest - clone.specifiedDirection = specifiedDirection - clone - } - - /** Connect this $coll to that $coll mono-directionally and element-wise. - * - * This uses the [[MonoConnect]] algorithm. - * - * @param that the $coll to connect to - * @group Connect - */ - final def := (that: Data)(implicit sourceInfo: SourceInfo, connectionCompileOptions: CompileOptions): Unit = this.connect(that)(sourceInfo, connectionCompileOptions) // scalastyle:ignore line.size.limit - - /** Connect this $coll to that $coll bi-directionally and element-wise. - * - * This uses the [[BiConnect]] algorithm. - * - * @param that the $coll to connect to - * @group Connect - */ - final def <> (that: Data)(implicit sourceInfo: SourceInfo, connectionCompileOptions: CompileOptions): Unit = this.bulkConnect(that)(sourceInfo, connectionCompileOptions) // scalastyle:ignore line.size.limit - - @chiselRuntimeDeprecated - @deprecated("litArg is deprecated, use litOption or litTo*Option", "3.2") - def litArg(): Option[LitArg] = topBindingOpt match { - case Some(ElementLitBinding(litArg)) => Some(litArg) - case Some(BundleLitBinding(litMap)) => None // this API does not support Bundle literals - case _ => None - } - - def isLit(): Boolean = litOption.isDefined - - /** - * If this is a literal that is representable as bits, returns the value as a BigInt. - * If not a literal, or not representable as bits (for example, is or contains Analog), returns None. - */ - def litOption(): Option[BigInt] - - /** - * Returns the literal value if this is a literal that is representable as bits, otherwise crashes. - */ - def litValue(): BigInt = litOption.get - - /** Returns the width, in bits, if currently known. */ - final def getWidth: Int = - if (isWidthKnown) width.get else throwException(s"Width of $this is unknown!") - /** Returns whether the width is currently known. */ - final def isWidthKnown: Boolean = width.known - /** Returns Some(width) if the width is known, else None. */ - final def widthOption: Option[Int] = if (isWidthKnown) Some(getWidth) else None - - /** Does a reinterpret cast of the bits in this node into the format that provides. - * Returns a new Wire of that type. Does not modify existing nodes. - * - * x.asTypeOf(that) performs the inverse operation of x := that.toBits. - * - * @note bit widths are NOT checked, may pad or drop bits from input - * @note that should have known widths - */ - def asTypeOf[T <: Data](that: T): T = macro SourceInfoTransform.thatArg - - /** @group SourceInfoTransformMacro */ - def do_asTypeOf[T <: Data](that: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = { - val thatCloned = Wire(that.cloneTypeFull) - thatCloned.connectFromBits(this.asUInt()) - thatCloned - } - - /** Assigns this node from Bits type. Internal implementation for asTypeOf. - */ - private[chisel3] def connectFromBits(that: Bits)(implicit sourceInfo: SourceInfo, - compileOptions: CompileOptions): Unit - - /** Reinterpret cast to UInt. - * - * @note value not guaranteed to be preserved: for example, a SInt of width - * 3 and value -1 (0b111) would become an UInt with value 7 - * @note Aggregates are recursively packed with the first element appearing - * in the least-significant bits of the result. - */ - final def asUInt(): UInt = macro SourceInfoTransform.noArg - - /** @group SourceInfoTransformMacro */ - def do_asUInt(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt - - /** Default pretty printing */ - def toPrintable: Printable -} - -trait WireFactory { - /** Construct a [[Wire]] from a type template - * @param t The template from which to construct this wire - */ - def apply[T <: Data](t: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = { - if (compileOptions.declaredTypeMustBeUnbound) { - requireIsChiselType(t, "wire type") - } - val x = t.cloneTypeFull - - // Bind each element of x to being a Wire - x.bind(WireBinding(Builder.forcedUserModule)) - - pushCommand(DefWire(sourceInfo, x)) - if (!compileOptions.explicitInvalidate) { - pushCommand(DefInvalid(sourceInfo, x.ref)) - } - - x - } -} - -/** Utility for constructing hardware wires - * - * The width of a `Wire` (inferred or not) is copied from the type template - * {{{ - * val w0 = Wire(UInt()) // width is inferred - * val w1 = Wire(UInt(8.W)) // width is set to 8 - * - * val w2 = Wire(Vec(4, UInt())) // width is inferred - * val w3 = Wire(Vec(4, UInt(8.W))) // width of each element is set to 8 - * - * class MyBundle { - * val unknown = UInt() - * val known = UInt(8.W) - * } - * val w4 = Wire(new MyBundle) - * // Width of w4.unknown is inferred - * // Width of w4.known is set to 8 - * }}} - * - */ -object Wire extends WireFactory - -/** Utility for constructing hardware wires with a default connection - * - * The two forms of `WireDefault` differ in how the type and width of the resulting [[Wire]] are - * specified. - * - * ==Single Argument== - * The single argument form uses the argument to specify both the type and default connection. For - * non-literal [[Bits]], the width of the [[Wire]] will be inferred. For literal [[Bits]] and all - * non-Bits arguments, the type will be copied from the argument. See the following examples for - * more details: - * - * 1. Literal [[Bits]] initializer: width will be set to match - * {{{ - * val w1 = WireDefault(1.U) // width will be inferred to be 1 - * val w2 = WireDefault(1.U(8.W)) // width is set to 8 - * }}} - * - * 2. Non-Literal [[Element]] initializer - width will be inferred - * {{{ - * val x = Wire(UInt()) - * val y = Wire(UInt(8.W)) - * val w1 = WireDefault(x) // width will be inferred - * val w2 = WireDefault(y) // width will be inferred - * }}} - * - * 3. [[Aggregate]] initializer - width will be set to match the aggregate - * - * {{{ - * class MyBundle { - * val unknown = UInt() - * val known = UInt(8.W) - * } - * val w1 = Wire(new MyBundle) - * val w2 = WireDefault(w1) - * // Width of w2.unknown is inferred - * // Width of w2.known is set to 8 - * }}} - * - * ==Double Argument== - * The double argument form allows the type of the [[Wire]] and the default connection to be - * specified independently. - * - * The width inference semantics for `WireDefault` with two arguments match those of [[Wire]]. The - * first argument to `WireDefault` is the type template which defines the width of the `Wire` in - * exactly the same way as the only argument to [[Wire]]. - * - * More explicitly, you can reason about `WireDefault` with multiple arguments as if it were defined - * as: - * {{{ - * def WireDefault[T <: Data](t: T, init: T): T = { - * val x = Wire(t) - * x := init - * x - * } - * }}} - * - * @note The `Default` in `WireDefault` refers to a `default` connection. This is in contrast to - * [[RegInit]] where the `Init` refers to a value on reset. - */ -object WireDefault { - - private def applyImpl[T <: Data](t: T, init: Data)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = { // scalastyle:ignore line.size.limit - implicit val noSourceInfo = UnlocatableSourceInfo - val x = Wire(t) - requireIsHardware(init, "wire initializer") - x := init - x - } - - /** Construct a [[Wire]] with a type template and a [[chisel3.DontCare]] default - * @param t The type template used to construct this [[Wire]] - * @param init The default connection to this [[Wire]], can only be [[DontCare]] - * @note This is really just a specialized form of `apply[T <: Data](t: T, init: T): T` with [[DontCare]] as `init` - */ - def apply[T <: Data](t: T, init: DontCare.type)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = { // scalastyle:ignore line.size.limit - applyImpl(t, init) - } - - /** Construct a [[Wire]] with a type template and a default connection - * @param t The type template used to construct this [[Wire]] - * @param init The hardware value that will serve as the default value - */ - def apply[T <: Data](t: T, init: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = { - applyImpl(t, init) - } - - /** Construct a [[Wire]] with a default connection - * @param init The hardware value that will serve as a type template and default value - */ - def apply[T <: Data](init: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = { - val model = (init match { - // If init is a literal without forced width OR any non-literal, let width be inferred - case init: Bits if !init.litIsForcedWidth.getOrElse(false) => init.cloneTypeWidth(Width()) - case _ => init.cloneTypeFull - }).asInstanceOf[T] - apply(model, init) - } -} - -package internal { - /** RHS (source) for Invalidate API. - * Causes connection logic to emit a DefInvalid when connected to an output port (or wire). - */ - private[chisel3] object InternalDontCare extends Element { - // This object should be initialized before we execute any user code that refers to it, - // otherwise this "Chisel" object will end up on the UserModule's id list. - // We make it private to chisel3 so it has to be accessed through the package object. - - private[chisel3] override val width: Width = UnknownWidth() - - bind(DontCareBinding(), SpecifiedDirection.Output) - override def cloneType: this.type = DontCare - - override def toString: String = "DontCare()" - - override def litOption: Option[BigInt] = None - - def toPrintable: Printable = PString("DONTCARE") - - private[chisel3] def connectFromBits(that: Bits)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Unit = { // scalastyle:ignore line.size.limit - Builder.error("connectFromBits: DontCare cannot be a connection sink (LHS)") - } - - def do_asUInt(implicit sourceInfo: chisel3.internal.sourceinfo.SourceInfo, compileOptions: CompileOptions): UInt = { // scalastyle:ignore line.size.limit - Builder.error("DontCare does not have a UInt representation") - 0.U - } - // DontCare's only match themselves. - private[chisel3] def typeEquivalent(that: Data): Boolean = that == DontCare - } -} diff --git a/chiselFrontend/src/main/scala/chisel3/Element.scala b/chiselFrontend/src/main/scala/chisel3/Element.scala deleted file mode 100644 index fccae9ab..00000000 --- a/chiselFrontend/src/main/scala/chisel3/Element.scala +++ /dev/null @@ -1,62 +0,0 @@ -// See LICENSE for license details. - -package chisel3 - -import chisel3.internal.Builder.pushCommand -import chisel3.internal.firrtl._ -import chisel3.internal.sourceinfo.SourceInfo -import chisel3.internal._ - -/** Element is a leaf data type: it cannot contain other [[Data]] objects. Example uses are for representing primitive - * data types, like integers and bits. - * - * @define coll element - */ -abstract class Element extends Data { - private[chisel3] final def allElements: Seq[Element] = Seq(this) - def widthKnown: Boolean = width.known - def name: String = getRef.name - - private[chisel3] override def bind(target: Binding, parentDirection: SpecifiedDirection) { - binding = target - val resolvedDirection = SpecifiedDirection.fromParent(parentDirection, specifiedDirection) - direction = ActualDirection.fromSpecified(resolvedDirection) - } - - private[chisel3] override def topBindingOpt: Option[TopBinding] = super.topBindingOpt match { - // Translate Bundle lit bindings to Element lit bindings - case Some(BundleLitBinding(litMap)) => litMap.get(this) match { - case Some(litArg) => Some(ElementLitBinding(litArg)) - case _ => Some(DontCareBinding()) - } - case topBindingOpt => topBindingOpt - } - - private[chisel3] def litArgOption: Option[LitArg] = topBindingOpt match { - case Some(ElementLitBinding(litArg)) => Some(litArg) - case _ => None - } - - override def litOption: Option[BigInt] = litArgOption.map(_.num) - private[chisel3] def litIsForcedWidth: Option[Boolean] = litArgOption.map(_.forcedWidth) - - // provide bits-specific literal handling functionality here - override private[chisel3] def ref: Arg = topBindingOpt match { - case Some(ElementLitBinding(litArg)) => litArg - case Some(BundleLitBinding(litMap)) => litMap.get(this) match { - case Some(litArg) => litArg - case _ => throwException(s"internal error: DontCare should be caught before getting ref") - } - case _ => super.ref - } - - private[chisel3] def legacyConnect(that: Data)(implicit sourceInfo: SourceInfo): Unit = { - // If the source is a DontCare, generate a DefInvalid for the sink, - // otherwise, issue a Connect. - if (that == DontCare) { - pushCommand(DefInvalid(sourceInfo, Node(this))) - } else { - pushCommand(Connect(sourceInfo, Node(this), that.ref)) - } - } -} diff --git a/chiselFrontend/src/main/scala/chisel3/Mem.scala b/chiselFrontend/src/main/scala/chisel3/Mem.scala deleted file mode 100644 index 24ab4b8e..00000000 --- a/chiselFrontend/src/main/scala/chisel3/Mem.scala +++ /dev/null @@ -1,216 +0,0 @@ -// See LICENSE for license details. - -package chisel3 - -import scala.language.experimental.macros - -import firrtl.{ir => fir} - -import chisel3.internal._ -import chisel3.internal.Builder.pushCommand -import chisel3.internal.firrtl._ -import chisel3.internal.sourceinfo.{SourceInfo, SourceInfoTransform, UnlocatableSourceInfo, MemTransform} - - -object Mem { - - /** Creates a combinational/asynchronous-read, sequential/synchronous-write [[Mem]]. - * - * @param size number of elements in the memory - * @param t data type of memory element - */ - def apply[T <: Data](size: BigInt, t: T): Mem[T] = macro MemTransform.apply[T] - - /** Creates a combinational/asynchronous-read, sequential/synchronous-write [[Mem]]. - * - * @param size number of elements in the memory - * @param t data type of memory element - */ - def apply[T <: Data](size: Int, t: T): Mem[T] = macro MemTransform.apply[T] - - /** @group SourceInfoTransformMacro */ - def do_apply[T <: Data](size: BigInt, t: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Mem[T] = { - if (compileOptions.declaredTypeMustBeUnbound) { - requireIsChiselType(t, "memory type") - } - val mt = t.cloneTypeFull - val mem = new Mem(mt, size) - pushCommand(DefMemory(sourceInfo, mem, mt, size)) - mem - } - - /** @group SourceInfoTransformMacro */ - def do_apply[T <: Data](size: Int, t: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Mem[T] = - do_apply(BigInt(size), t)(sourceInfo, compileOptions) -} - -sealed abstract class MemBase[T <: Data](val t: T, val length: BigInt) extends HasId with NamedComponent with SourceInfoDoc { - // REVIEW TODO: make accessors (static/dynamic, read/write) combinations consistent. - - /** Creates a read accessor into the memory with static addressing. See the - * class documentation of the memory for more detailed information. - */ - def apply(x: BigInt): T = macro SourceInfoTransform.xArg - - /** Creates a read accessor into the memory with static addressing. See the - * class documentation of the memory for more detailed information. - */ - def apply(x: Int): T = macro SourceInfoTransform.xArg - - /** @group SourceInfoTransformMacro */ - def do_apply(idx: BigInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = { - require(idx >= 0 && idx < length) - apply(idx.asUInt) - } - - /** @group SourceInfoTransformMacro */ - def do_apply(idx: Int)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = - do_apply(BigInt(idx))(sourceInfo, compileOptions) - - /** Creates a read/write accessor into the memory with dynamic addressing. - * See the class documentation of the memory for more detailed information. - */ - def apply(x: UInt): T = macro SourceInfoTransform.xArg - - /** @group SourceInfoTransformMacro */ - def do_apply(idx: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = - makePort(sourceInfo, idx, MemPortDirection.INFER) - - /** Creates a read accessor into the memory with dynamic addressing. See the - * class documentation of the memory for more detailed information. - */ - def read(x: UInt): T = macro SourceInfoTransform.xArg - - /** @group SourceInfoTransformMacro */ - def do_read(idx: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = - makePort(sourceInfo, idx, MemPortDirection.READ) - - /** Creates a write accessor into the memory. - * - * @param idx memory element index to write into - * @param data new data to write - */ - def write(idx: UInt, data: T)(implicit compileOptions: CompileOptions): Unit = { - implicit val sourceInfo = UnlocatableSourceInfo - makePort(UnlocatableSourceInfo, idx, MemPortDirection.WRITE) := data - } - - /** Creates a masked write accessor into the memory. - * - * @param idx memory element index to write into - * @param data new data to write - * @param mask write mask as a Seq of Bool: a write to the Vec element in - * memory is only performed if the corresponding mask index is true. - * - * @note this is only allowed if the memory's element data type is a Vec - */ - def write(idx: UInt, data: T, mask: Seq[Bool]) (implicit evidence: T <:< Vec[_], compileOptions: CompileOptions): Unit = { - implicit val sourceInfo = UnlocatableSourceInfo - val accessor = makePort(sourceInfo, idx, MemPortDirection.WRITE).asInstanceOf[Vec[Data]] - val dataVec = data.asInstanceOf[Vec[Data]] - if (accessor.length != dataVec.length) { - Builder.error(s"Mem write data must contain ${accessor.length} elements (found ${dataVec.length})") - } - if (accessor.length != mask.length) { - Builder.error(s"Mem write mask must contain ${accessor.length} elements (found ${mask.length})") - } - for (((cond, port), datum) <- mask zip accessor zip dataVec) - when (cond) { port := datum } - } - - private def makePort(sourceInfo: SourceInfo, idx: UInt, dir: MemPortDirection)(implicit compileOptions: CompileOptions): T = { - requireIsHardware(idx, "memory port index") - val i = Vec.truncateIndex(idx, length)(sourceInfo, compileOptions) - - val port = pushCommand( - DefMemPort(sourceInfo, - t.cloneTypeFull, Node(this), dir, i.ref, Builder.forcedClock.ref) - ).id - // Bind each element of port to being a MemoryPort - port.bind(MemoryPortBinding(Builder.forcedUserModule)) - port - } -} - -/** A combinational/asynchronous-read, sequential/synchronous-write memory. - * - * Writes take effect on the rising clock edge after the request. Reads are - * combinational (requests will return data on the same cycle). - * Read-after-write hazards are not an issue. - * - * @note when multiple conflicting writes are performed on a Mem element, the - * result is undefined (unlike Vec, where the last assignment wins) - */ -sealed class Mem[T <: Data] private (t: T, length: BigInt) extends MemBase(t, length) - -object SyncReadMem { - - - type ReadUnderWrite = fir.ReadUnderWrite.Value - val Undefined = fir.ReadUnderWrite.Undefined - val ReadFirst = fir.ReadUnderWrite.Old - val WriteFirst = fir.ReadUnderWrite.New - - /** Creates a sequential/synchronous-read, sequential/synchronous-write [[SyncReadMem]]. - * - * @param size number of elements in the memory - * @param t data type of memory element - */ - def apply[T <: Data](size: BigInt, t: T): SyncReadMem[T] = macro MemTransform.apply[T] - def apply[T <: Data](size: BigInt, t: T, ruw: ReadUnderWrite): SyncReadMem[T] = macro MemTransform.apply_ruw[T] - - /** Creates a sequential/synchronous-read, sequential/synchronous-write [[SyncReadMem]]. - * - * @param size number of elements in the memory - * @param t data type of memory element - */ - def apply[T <: Data](size: Int, t: T): SyncReadMem[T] = macro MemTransform.apply[T] - def apply[T <: Data](size: Int, t: T, ruw: ReadUnderWrite): SyncReadMem[T] = macro MemTransform.apply_ruw[T] - - /** @group SourceInfoTransformMacro */ - def do_apply[T <: Data](size: BigInt, t: T, ruw: ReadUnderWrite = Undefined)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SyncReadMem[T] = { - if (compileOptions.declaredTypeMustBeUnbound) { - requireIsChiselType(t, "memory type") - } - val mt = t.cloneTypeFull - val mem = new SyncReadMem(mt, size, ruw) - pushCommand(DefSeqMemory(sourceInfo, mem, mt, size, ruw)) - mem - } - - /** @group SourceInfoTransformMacro */ - // Alternate signatures can't use default parameter values - def do_apply[T <: Data](size: Int, t: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SyncReadMem[T] = - do_apply(BigInt(size), t)(sourceInfo, compileOptions) - - /** @group SourceInfoTransformMacro */ - // Alternate signatures can't use default parameter values - def do_apply[T <: Data](size: Int, t: T, ruw: ReadUnderWrite)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SyncReadMem[T] = - do_apply(BigInt(size), t, ruw)(sourceInfo, compileOptions) -} - -/** A sequential/synchronous-read, sequential/synchronous-write memory. - * - * Writes take effect on the rising clock edge after the request. Reads return - * data on the rising edge after the request. Read-after-write behavior (when - * a read and write to the same address are requested on the same cycle) is - * undefined. - * - * @note when multiple conflicting writes are performed on a Mem element, the - * result is undefined (unlike Vec, where the last assignment wins) - */ -sealed class SyncReadMem[T <: Data] private (t: T, n: BigInt, val readUnderWrite: SyncReadMem.ReadUnderWrite) extends MemBase[T](t, n) { - def read(x: UInt, en: Bool): T = macro SourceInfoTransform.xEnArg - - /** @group SourceInfoTransformMacro */ - def do_read(addr: UInt, enable: Bool)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = { - val a = Wire(UInt()) - a := DontCare - var port: Option[T] = None - when (enable) { - a := addr - port = Some(read(a)) - } - port.get - } -} diff --git a/chiselFrontend/src/main/scala/chisel3/Module.scala b/chiselFrontend/src/main/scala/chisel3/Module.scala deleted file mode 100644 index f1c4e30a..00000000 --- a/chiselFrontend/src/main/scala/chisel3/Module.scala +++ /dev/null @@ -1,405 +0,0 @@ -// See LICENSE for license details. - -package chisel3 - -import scala.collection.immutable.ListMap -import scala.collection.mutable.{ArrayBuffer, HashMap} -import scala.collection.JavaConversions._ -import scala.language.experimental.macros - -import java.util.IdentityHashMap - -import chisel3.internal._ -import chisel3.internal.Builder._ -import chisel3.internal.firrtl._ -import chisel3.internal.sourceinfo.{InstTransform, SourceInfo} -import chisel3.experimental.BaseModule -import _root_.firrtl.annotations.{ModuleName, ModuleTarget, IsModule} - -object Module extends SourceInfoDoc { - /** A wrapper method that all Module instantiations must be wrapped in - * (necessary to help Chisel track internal state). - * - * @param bc the Module being created - * - * @return the input module `m` with Chisel metadata properly set - */ - def apply[T <: BaseModule](bc: => T): T = macro InstTransform.apply[T] - - /** @group SourceInfoTransformMacro */ - def do_apply[T <: BaseModule](bc: => T) - (implicit sourceInfo: SourceInfo, - compileOptions: CompileOptions): T = { - if (Builder.readyForModuleConstr) { - throwException("Error: Called Module() twice without instantiating a Module." + - sourceInfo.makeMessage(" See " + _)) - } - Builder.readyForModuleConstr = true - - val parent = Builder.currentModule - val whenDepth: Int = Builder.whenDepth - - // Save then clear clock and reset to prevent leaking scope, must be set again in the Module - val (saveClock, saveReset) = (Builder.currentClock, Builder.currentReset) - Builder.currentClock = None - Builder.currentReset = None - - // Execute the module, this has the following side effects: - // - set currentModule - // - unset readyForModuleConstr - // - reset whenDepth to 0 - // - set currentClockAndReset - val module: T = bc // bc is actually evaluated here - - if (Builder.whenDepth != 0) { - throwException("Internal Error! when() scope depth is != 0, this should have been caught!") - } - if (Builder.readyForModuleConstr) { - throwException("Error: attempted to instantiate a Module, but nothing happened. " + - "This is probably due to rewrapping a Module instance with Module()." + - sourceInfo.makeMessage(" See " + _)) - } - Builder.currentModule = parent // Back to parent! - Builder.whenDepth = whenDepth - Builder.currentClock = saveClock // Back to clock and reset scope - Builder.currentReset = saveReset - - val component = module.generateComponent() - Builder.components += component - - // Handle connections at enclosing scope - if(!Builder.currentModule.isEmpty) { - pushCommand(DefInstance(sourceInfo, module, component.ports)) - module.initializeInParent(compileOptions) - } - module - } - - /** Returns the implicit Clock */ - def clock: Clock = Builder.forcedClock - /** Returns the implicit Reset */ - def reset: Reset = Builder.forcedReset - /** Returns the current Module */ - def currentModule: Option[BaseModule] = Builder.currentModule -} - -package experimental { - - object IO { - /** Constructs a port for the current Module - * - * This must wrap the datatype used to set the io field of any Module. - * i.e. All concrete modules must have defined io in this form: - * [lazy] val io[: io type] = IO(...[: io type]) - * - * Items in [] are optional. - * - * The granted iodef must be a chisel type and not be bound to hardware. - * - * Also registers a Data as a port, also performing bindings. Cannot be called once ports are - * requested (so that all calls to ports will return the same information). - * Internal API. - */ - def apply[T<:Data](iodef: T): T = { - val module = Module.currentModule.get // Impossible to fail - require(!module.isClosed, "Can't add more ports after module close") - requireIsChiselType(iodef, "io type") - - // Clone the IO so we preserve immutability of data types - val iodefClone = try { - iodef.cloneTypeFull - } catch { - // For now this is going to be just a deprecation so we don't suddenly break everyone's code - case e: AutoClonetypeException => - Builder.deprecated(e.getMessage, Some(s"${iodef.getClass}")) - iodef - } - module.bindIoInPlace(iodefClone) - iodefClone - } - } -} - -package internal { - import chisel3.experimental.BaseModule - - object BaseModule { - private[chisel3] class ClonePorts (elts: Data*)(implicit compileOptions: CompileOptions) extends Record { - val elements = ListMap(elts.map(d => d.instanceName -> d.cloneTypeFull): _*) - def apply(field: String) = elements(field) - override def cloneType = (new ClonePorts(elts: _*)).asInstanceOf[this.type] - } - - private[chisel3] def cloneIORecord(proto: BaseModule)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): ClonePorts = { - require(proto.isClosed, "Can't clone a module before module close") - val clonePorts = new ClonePorts(proto.getModulePorts: _*) - clonePorts.bind(WireBinding(Builder.forcedUserModule)) - val cloneInstance = new DefInstance(sourceInfo, proto, proto._component.get.ports) { - override def name = clonePorts.getRef.name - } - pushCommand(cloneInstance) - if (!compileOptions.explicitInvalidate) { - pushCommand(DefInvalid(sourceInfo, clonePorts.ref)) - } - if (proto.isInstanceOf[MultiIOModule]) { - clonePorts("clock") := Module.clock - clonePorts("reset") := Module.reset - } - clonePorts - } - } -} - -package experimental { - - /** Abstract base class for Modules, an instantiable organizational unit for RTL. - */ - // TODO: seal this? - abstract class BaseModule extends HasId { - // - // Builder Internals - this tracks which Module RTL construction belongs to. - // - if (!Builder.readyForModuleConstr) { - throwException("Error: attempted to instantiate a Module without wrapping it in Module().") - } - readyForModuleConstr = false - - Builder.currentModule = Some(this) - Builder.whenDepth = 0 - - // - // Module Construction Internals - // - protected var _closed = false - - /** Internal check if a Module is closed */ - private[chisel3] def isClosed = _closed - - // Fresh Namespace because in Firrtl, Modules namespaces are disjoint with the global namespace - private[chisel3] val _namespace = Namespace.empty - private val _ids = ArrayBuffer[HasId]() - private[chisel3] def addId(d: HasId) { - if (Builder.aspectModule(this).isDefined) { - aspectModule(this).get.addId(d) - } else { - require(!_closed, "Can't write to module after module close") - _ids += d - } - } - - protected def getIds = { - require(_closed, "Can't get ids before module close") - _ids.toSeq - } - - private val _ports = new ArrayBuffer[Data]() - - // getPorts unfortunately already used for tester compatibility - protected[chisel3] def getModulePorts = { - require(_closed, "Can't get ports before module close") - _ports.toSeq - } - - // These methods allow checking some properties of ports before the module is closed, - // mainly for compatibility purposes. - protected def portsContains(elem: Data): Boolean = _ports contains elem - - protected def portsSize: Int = _ports.size - - /** Generates the FIRRTL Component (Module or Blackbox) of this Module. - * Also closes the module so no more construction can happen inside. - */ - private[chisel3] def generateComponent(): Component - - /** Sets up this module in the parent context - */ - private[chisel3] def initializeInParent(parentCompileOptions: CompileOptions): Unit - - // - // Chisel Internals - // - - /** The desired name of this module (which will be used in generated FIRRTL IR or Verilog). - * - * The name of a module approximates the behavior of the Java Reflection [[`getSimpleName` method - * https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html#getSimpleName--]] with some modifications: - * - * - Anonymous modules will get an `"_Anon"` tag - * - Modules defined in functions will use their class name and not a numeric name - * - * @note If you want a custom or parametric name, override this method. - */ - def desiredName: String = { - /* The default module name is derived from the Java reflection derived class name. */ - val baseName = this.getClass.getName - - /* A sequence of string filters applied to the name */ - val filters: Seq[String => String] = Seq( - ((a: String) => raw"\$$+anon".r.replaceAllIn(a, "_Anon")) // Merge the "$$anon" name with previous name - ) - - filters - .foldLeft(baseName){ case (str, filter) => filter(str) } // 1. Apply filters to baseName - .split("\\.|\\$") // 2. Split string at '.' or '$' - .filterNot(_.forall(_.isDigit)) // 3. Drop purely numeric names - .last // 4. Use the last name - } - - /** Legalized name of this module. */ - final lazy val name = try { - Builder.globalNamespace.name(desiredName) - } catch { - case e: NullPointerException => throwException( - s"Error: desiredName of ${this.getClass.getName} is null. Did you evaluate 'name' before all values needed by desiredName were available?", e) // scalastyle:ignore line.size.limit - case t: Throwable => throw t - } - - /** Returns a FIRRTL ModuleName that references this object - * - * @note Should not be called until circuit elaboration is complete - */ - final def toNamed: ModuleName = toTarget.toNamed - - /** Returns a FIRRTL ModuleTarget that references this object - * - * @note Should not be called until circuit elaboration is complete - */ - final def toTarget: ModuleTarget = ModuleTarget(this.circuitName, this.name) - - /** Returns a FIRRTL ModuleTarget that references this object - * - * @note Should not be called until circuit elaboration is complete - */ - final def toAbsoluteTarget: IsModule = { - _parent match { - case Some(parent) => parent.toAbsoluteTarget.instOf(this.instanceName, toTarget.module) - case None => toTarget - } - } - - /** - * Internal API. Returns a list of this module's generated top-level ports as a map of a String - * (FIRRTL name) to the IO object. Only valid after the module is closed. - * - * Note: for BlackBoxes (but not ExtModules), this returns the contents of the top-level io - * object, consistent with what is emitted in FIRRTL. - * - * TODO: Use SeqMap/VectorMap when those data structures become available. - */ - private[chisel3] def getChiselPorts: Seq[(String, Data)] = { - require(_closed, "Can't get ports before module close") - _component.get.ports.map { port => - (port.id.getRef.asInstanceOf[ModuleIO].name, port.id) - } - } - - /** Called at the Module.apply(...) level after this Module has finished elaborating. - * Returns a map of nodes -> names, for named nodes. - * - * Helper method. - */ - protected def nameIds(rootClass: Class[_]): HashMap[HasId, String] = { - val names = new HashMap[HasId, String]() - - def name(node: HasId, name: String) { - // First name takes priority, like suggestName - // TODO: DRYify with suggestName - if (!names.contains(node)) { - names.put(node, name) - } - } - - /** Scala generates names like chisel3$util$Queue$$ram for private vals - * This extracts the part after $$ for names like this and leaves names - * without $$ unchanged - */ - def cleanName(name: String): String = name.split("""\$\$""").lastOption.getOrElse(name) - - for (m <- getPublicFields(rootClass)) { - Builder.nameRecursively(cleanName(m.getName), m.invoke(this), name) - } - - names - } - - /** Compatibility function. Allows Chisel2 code which had ports without the IO wrapper to - * compile under Bindings checks. Does nothing in non-compatibility mode. - * - * Should NOT be used elsewhere. This API will NOT last. - * - * TODO: remove this, perhaps by removing Bindings checks in compatibility mode. - */ - def _compatAutoWrapPorts() {} // scalastyle:ignore method.name - - /** Chisel2 code didn't require the IO(...) wrapper and would assign a Chisel type directly to - * io, then do operations on it. This binds a Chisel type in-place (mutably) as an IO. - */ - protected def _bindIoInPlace(iodef: Data): Unit = { // scalastyle:ignore method.name - // 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.specifiedDirection match { - // Recurse into children to ensure explicit direction set somewhere - case SpecifiedDirection.Unspecified | SpecifiedDirection.Flip => data match { - case record: Record => - val compatRecord = !record.compileOptions.dontAssumeDirectionality - record.getElements.foreach(assignCompatDir(_, compatRecord)) - case vec: Vec[_] => - vec.getElements.foreach(assignCompatDir(_, insideCompat)) - } - case SpecifiedDirection.Input | SpecifiedDirection.Output => // forced assign, nothing to do - } - } - } - - assignCompatDir(iodef, false) - - iodef.bind(PortBinding(this)) - _ports += iodef - } - - /** Private accessor for _bindIoInPlace */ - private[chisel3] def bindIoInPlace(iodef: Data): Unit = _bindIoInPlace(iodef) - - /** - * This must wrap the datatype used to set the io field of any Module. - * i.e. All concrete modules must have defined io in this form: - * [lazy] val io[: io type] = IO(...[: io type]) - * - * Items in [] are optional. - * - * The granted iodef must be a chisel type and not be bound to hardware. - * - * Also registers a Data as a port, also performing bindings. Cannot be called once ports are - * requested (so that all calls to ports will return the same information). - * Internal API. - * - * TODO(twigg): Specifically walk the Data definition to call out which nodes - * are problematic. - */ - protected def IO[T <: Data](iodef: T): T = chisel3.experimental.IO.apply(iodef) // scalastyle:ignore method.name - - // - // Internal Functions - // - - /** Keep component for signal names */ - private[chisel3] var _component: Option[Component] = None - - /** Signal name (for simulation). */ - override def instanceName: String = - if (_parent == None) name else _component match { - case None => getRef.name - case Some(c) => getRef fullName c - } - - } -} diff --git a/chiselFrontend/src/main/scala/chisel3/ModuleAspect.scala b/chiselFrontend/src/main/scala/chisel3/ModuleAspect.scala deleted file mode 100644 index 20793cd7..00000000 --- a/chiselFrontend/src/main/scala/chisel3/ModuleAspect.scala +++ /dev/null @@ -1,26 +0,0 @@ -// See LICENSE for license details. - -package chisel3 - -import chisel3.internal.Builder - -/** Used by Chisel Aspects to inject Chisel code into modules, after they have been elaborated. - * This is an internal API - don't use! - * - * It adds itself as an aspect to the module, which allows proper checking of connection and binding legality. - * - * @param module Module for which this object is an aspect of - * @param moduleCompileOptions - */ -abstract class ModuleAspect private[chisel3] (module: RawModule) - (implicit moduleCompileOptions: CompileOptions) extends RawModule { - - Builder.addAspect(module, this) - - override def circuitName: String = module.toTarget.circuit - - override def desiredName: String = module.name - - override val _namespace = module._namespace -} - diff --git a/chiselFrontend/src/main/scala/chisel3/MultiClock.scala b/chiselFrontend/src/main/scala/chisel3/MultiClock.scala deleted file mode 100644 index 239e745a..00000000 --- a/chiselFrontend/src/main/scala/chisel3/MultiClock.scala +++ /dev/null @@ -1,70 +0,0 @@ -// See LICENSE for license details. - -package chisel3 - -import chisel3.internal._ - -import scala.language.experimental.macros - -object withClockAndReset { // scalastyle:ignore object.name - /** Creates a new Clock and Reset scope - * - * @param clock the new implicit Clock - * @param reset the new implicit Reset - * @param block the block of code to run with new implicit Clock and Reset - * @return the result of the block - */ - def apply[T](clock: Clock, reset: Reset)(block: => T): T = { - // Save parentScope - val parentClock = Builder.currentClock - val parentReset = Builder.currentReset - - Builder.currentClock = Some(clock) - Builder.currentReset = Some(reset) - - val res = block // execute block - - // Return to old scope - Builder.currentClock = parentClock - Builder.currentReset = parentReset - res - } -} - -object withClock { // scalastyle:ignore object.name - /** Creates a new Clock scope - * - * @param clock the new implicit Clock - * @param block the block of code to run with new implicit Clock - * @return the result of the block - */ - def apply[T](clock: Clock)(block: => T): T = { - // Save parentScope - val parentClock = Builder.currentClock - Builder.currentClock = Some(clock) - val res = block // execute block - // Return to old scope - Builder.currentClock = parentClock - res - } -} - -object withReset { // scalastyle:ignore object.name - /** Creates a new Reset scope - * - * @param reset the new implicit Reset - * @param block the block of code to run with new implicit Reset - * @return the result of the block - */ - def apply[T](reset: Reset)(block: => T): T = { - // Save parentScope - val parentReset = Builder.currentReset - Builder.currentReset = Some(reset) - val res = block // execute block - // Return to old scope - Builder.currentReset = parentReset - res - } - -} - diff --git a/chiselFrontend/src/main/scala/chisel3/Mux.scala b/chiselFrontend/src/main/scala/chisel3/Mux.scala deleted file mode 100644 index 960424bf..00000000 --- a/chiselFrontend/src/main/scala/chisel3/Mux.scala +++ /dev/null @@ -1,50 +0,0 @@ -// See LICENSE for license details. - -package chisel3 - -import scala.language.experimental.macros - -import chisel3.internal._ -import chisel3.internal.Builder.pushOp -import chisel3.internal.sourceinfo.{SourceInfo, MuxTransform} -import chisel3.internal.firrtl._ -import chisel3.internal.firrtl.PrimOp._ - -object Mux extends SourceInfoDoc { - /** Creates a mux, whose output is one of the inputs depending on the - * value of the condition. - * - * @param cond condition determining the input to choose - * @param con the value chosen when `cond` is true - * @param alt the value chosen when `cond` is false - * @example - * {{{ - * val muxOut = Mux(data_in === 3.U, 3.U(4.W), 0.U(4.W)) - * }}} - */ - def apply[T <: Data](cond: Bool, con: T, alt: T): T = macro MuxTransform.apply[T] - - /** @group SourceInfoTransformMacro */ - def do_apply[T <: Data](cond: Bool, con: T, alt: T)(implicit sourceInfo: SourceInfo, - compileOptions: CompileOptions): T = { - requireIsHardware(cond, "mux condition") - requireIsHardware(con, "mux true value") - requireIsHardware(alt, "mux false value") - val d = cloneSupertype(Seq(con, alt), "Mux") - val conRef = con match { // this matches chisel semantics (DontCare as object) to firrtl semantics (invalidate) - case DontCare => - val dcWire = Wire(d) - dcWire := DontCare - dcWire.ref - case _ => con.ref - } - val altRef = alt match { - case DontCare => - val dcWire = Wire(d) - dcWire := DontCare - dcWire.ref - case _ => alt.ref - } - pushOp(DefPrim(sourceInfo, d, MultiplexOp, cond.ref, conRef, altRef)) - } -} diff --git a/chiselFrontend/src/main/scala/chisel3/Num.scala b/chiselFrontend/src/main/scala/chisel3/Num.scala deleted file mode 100644 index 7a6b0744..00000000 --- a/chiselFrontend/src/main/scala/chisel3/Num.scala +++ /dev/null @@ -1,308 +0,0 @@ -// See LICENSE for license details. - -package chisel3 - -import chisel3.internal.ChiselException -import chisel3.internal.firrtl.{BinaryPoint, KnownBinaryPoint} - -import scala.language.experimental.macros -import chisel3.internal.sourceinfo.{SourceInfo, SourceInfoTransform} - -// scalastyle:off method.name - -// REVIEW TODO: Further discussion needed on what Num actually is. - -/** Abstract trait defining operations available on numeric-like hardware data types. - * - * @tparam T the underlying type of the number - * @groupdesc Arithmetic Arithmetic hardware operators - * @groupdesc Comparison Comparison hardware operators - * @groupdesc Logical Logical hardware operators - * @define coll numeric-like type - * @define numType hardware type - * @define canHaveHighCost can result in significant cycle time and area costs - * @define canGenerateA This method generates a - * @define singleCycleMul @note $canGenerateA fully combinational multiplier which $canHaveHighCost. - * @define singleCycleDiv @note $canGenerateA fully combinational divider which $canHaveHighCost. - * @define maxWidth @note The width of the returned $numType is `max(width of this, width of that)`. - * @define maxWidthPlusOne @note The width of the returned $numType is `max(width of this, width of that) + 1`. - * @define sumWidth @note The width of the returned $numType is `width of this` + `width of that`. - * @define unchangedWidth @note The width of the returned $numType is unchanged, i.e., the `width of this`. - */ -trait Num[T <: Data] { - self: Num[T] => - // def << (b: T): T - // def >> (b: T): T - //def unary_-(): T - - // REVIEW TODO: double check ops conventions against FIRRTL - - /** Addition operator - * - * @param that a $numType - * @return the sum of this $coll and `that` - * $maxWidth - * @group Arithmetic - */ - final def + (that: T): T = macro SourceInfoTransform.thatArg - - /** @group SourceInfoTransformMacro */ - def do_+ (that: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T - - /** Multiplication operator - * - * @param that a $numType - * @return the product of this $coll and `that` - * $sumWidth - * $singleCycleMul - * @group Arithmetic - */ - final def * (that: T): T = macro SourceInfoTransform.thatArg - - /** @group SourceInfoTransformMacro */ - def do_* (that: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T - - /** Division operator - * - * @param that a $numType - * @return the quotient of this $coll divided by `that` - * $singleCycleDiv - * @todo full rules - * @group Arithmetic - */ - final def / (that: T): T = macro SourceInfoTransform.thatArg - - /** @group SourceInfoTransformMacro */ - def do_/ (that: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T - - /** Modulo operator - * - * @param that a $numType - * @return the remainder of this $coll divided by `that` - * $singleCycleDiv - * @group Arithmetic - */ - final def % (that: T): T = macro SourceInfoTransform.thatArg - - /** @group SourceInfoTransformMacro */ - def do_% (that: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T - - /** Subtraction operator - * - * @param that a $numType - * @return the difference of this $coll less `that` - * $maxWidthPlusOne - * @group Arithmetic - */ - final def - (that: T): T = macro SourceInfoTransform.thatArg - - /** @group SourceInfoTransformMacro */ - def do_- (that: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T - - /** Less than operator - * - * @param that a $numType - * @return a hardware [[Bool]] asserted if this $coll is less than `that` - * @group Comparison - */ - final def < (that: T): Bool = macro SourceInfoTransform.thatArg - - /** @group SourceInfoTransformMacro */ - def do_< (that: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool - - /** Less than or equal to operator - * - * @param that a $numType - * @return a hardware [[Bool]] asserted if this $coll is less than or equal to `that` - * @group Comparison - */ - final def <= (that: T): Bool = macro SourceInfoTransform.thatArg - - /** @group SourceInfoTransformMacro */ - def do_<= (that: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool - - /** Greater than operator - * - * @param that a hardware component - * @return a hardware [[Bool]] asserted if this $coll is greater than `that` - * @group Comparison - */ - final def > (that: T): Bool = macro SourceInfoTransform.thatArg - - /** @group SourceInfoTransformMacro */ - def do_> (that: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool - - /** Greater than or equal to operator - * - * @param that a hardware component - * @return a hardware [[Bool]] asserted if this $coll is greather than or equal to `that` - * @group Comparison - */ - final def >= (that: T): Bool = macro SourceInfoTransform.thatArg - - /** @group SourceInfoTransformMacro */ - def do_>= (that: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool - - /** Absolute value operator - * - * @return a $numType with a value equal to the absolute value of this $coll - * $unchangedWidth - * @group Arithmetic - */ - final def abs(): T = macro SourceInfoTransform.noArg - - /** @group SourceInfoTransformMacro */ - def do_abs(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T - - /** Minimum operator - * - * @param that a hardware $coll - * @return a $numType with a value equal to the mimimum value of this $coll and `that` - * $maxWidth - * @group Arithmetic - */ - final def min(that: T): T = macro SourceInfoTransform.thatArg - - /** @group SourceInfoTransformMacro */ - def do_min(that: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = - Mux(this < that, this.asInstanceOf[T], that) - - /** Maximum operator - * - * @param that a $numType - * @return a $numType with a value equal to the mimimum value of this $coll and `that` - * $maxWidth - * @group Arithmetic - */ - final def max(that: T): T = macro SourceInfoTransform.thatArg - - /** @group SourceInfoTransformMacro */ - def do_max(that: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = - Mux(this < that, that, this.asInstanceOf[T]) -} - -object Num extends NumObject - -/** NumbObject has a lot of convenience methods for converting between - * BigInts and Double and BigDecimal - * For backwards compatibility this is used with FixedPoint and Interval objects - * but is better used with the Num Object - * - */ -trait NumObject { - val MaxBitsBigIntToBigDecimal = 108 - val MaxBitsBigIntToDouble = 53 - - /** - * How to create a bigint from a double with a specific binaryPoint - * @param x a double value - * @param binaryPoint a binaryPoint that you would like to use - * @return - */ - def toBigInt(x: Double, binaryPoint: Int): BigInt = { - val multiplier = math.pow(2, binaryPoint) - val result = BigInt(math.round(x * multiplier)) - result - } - - /** - * How to create a bigint from a big decimal with a specific binaryPoint - * @param x a BigDecimal value - * @param binaryPoint a binaryPoint that you would like to use - * @return - */ - def toBigInt(x: Double, binaryPoint: BinaryPoint): BigInt = { - binaryPoint match { - case KnownBinaryPoint(n) => toBigInt(x, n) - case x => - throw new ChiselException(s"Error converting Double $x to BigInt, binary point must be known, not $x") - } - } - - /** - * How to create a bigint from a big decimal with a specific binaryPoint (int) - * @param x a BigDecimal value - * @param binaryPoint a binaryPoint that you would like to use - * @return - */ - def toBigInt(x: BigDecimal, binaryPoint: Int): BigInt = { - val multiplier = math.pow(2, binaryPoint) - val result = (x * multiplier).rounded.toBigInt() - result - } - - /** - * How to create a bigint from a big decimal with a specific binaryPoint - * @param value a BigDecimal value - * @param binaryPoint a binaryPoint that you would like to use - * @return - */ - def toBigInt(value: BigDecimal, binaryPoint: BinaryPoint): BigInt = { - binaryPoint match { - case KnownBinaryPoint(n) => toBigInt(value, n) - case x => - throw new ChiselException(s"Error converting BigDecimal $value to BigInt, binary point must be known, not $x") - } - } - - /** - * converts a bigInt with the given binaryPoint into the double representation - * @param i a bigint - * @param binaryPoint the implied binaryPoint of @i - * @return - */ - def toDouble(i: BigInt, binaryPoint: Int): Double = { - if(i.bitLength >= 54) { - throw new ChiselException( - s"BigInt $i with bitlength ${i.bitLength} is too big, precision lost with > $MaxBitsBigIntToDouble bits" - ) - } - val multiplier = math.pow(2, binaryPoint) - val result = i.toDouble / multiplier - result - } - - /** - * converts a bigInt with the given binaryPoint into the double representation - * @param value a bigint - * @param binaryPoint the implied binaryPoint of @i - * @return - */ - def toDouble(value: BigInt, binaryPoint: BinaryPoint): Double = { - binaryPoint match { - case KnownBinaryPoint(n) => toDouble(value, n) - case x => - throw new ChiselException(s"Error converting BigDecimal $value to BigInt, binary point must be known, not $x") - } } - - /** - * converts a bigInt with the given binaryPoint into the BigDecimal representation - * @param value a bigint - * @param binaryPoint the implied binaryPoint of @i - * @return - */ - def toBigDecimal(value: BigInt, binaryPoint: Int): BigDecimal = { - if(value.bitLength > MaxBitsBigIntToBigDecimal) { - throw new ChiselException( - s"BigInt $value with bitlength ${value.bitLength} is too big, precision lost with > $MaxBitsBigIntToBigDecimal bits" - ) - } - val multiplier = BigDecimal(1.0) / BigDecimal(math.pow(2, binaryPoint)) - val result = BigDecimal(value) * multiplier - result - } - - /** - * converts a bigInt with the given binaryPoint into the BigDecimal representation - * @param value a bigint - * @param binaryPoint the implied binaryPoint of @i - * @return - */ - def toBigDecimal(value: BigInt, binaryPoint: BinaryPoint): BigDecimal = { - binaryPoint match { - case KnownBinaryPoint(n) => toBigDecimal(value, n) - case x => - throw new ChiselException(s"Error converting BigDecimal $value to BigInt, binary point must be known, not $x") - } - } -} \ No newline at end of file diff --git a/chiselFrontend/src/main/scala/chisel3/Printable.scala b/chiselFrontend/src/main/scala/chisel3/Printable.scala deleted file mode 100644 index 7add9166..00000000 --- a/chiselFrontend/src/main/scala/chisel3/Printable.scala +++ /dev/null @@ -1,178 +0,0 @@ -// See LICENSE for license details. - -package chisel3 - -import chisel3.internal.firrtl.Component - -import scala.collection.mutable - -import java.util.{ - MissingFormatArgumentException, - UnknownFormatConversionException -} - -/** Superclass of things that can be printed in the resulting circuit - * - * Usually created using the custom string interpolator `p"..."`. Printable string interpolation is - * similar to [[https://docs.scala-lang.org/overviews/core/string-interpolation.html String - * interpolation in Scala]] For example: - * {{{ - * printf(p"The value of wire = \$wire\n") - * }}} - * This is equivalent to writing: - * {{{ - * printf(p"The value of wire = %d\n", wire) - * }}} - * All Chisel data types have a method `.toPrintable` that gives a default pretty print that can be - * accessed via `p"..."`. This works even for aggregate types, for example: - * {{{ - * val myVec = VecInit(5.U, 10.U, 13.U) - * printf(p"myVec = \$myVec\n") - * // myVec = Vec(5, 10, 13) - * - * val myBundle = Wire(new Bundle { - * val foo = UInt() - * val bar = UInt() - * }) - * myBundle.foo := 3.U - * myBundle.bar := 11.U - * printf(p"myBundle = \$myBundle\n") - * // myBundle = Bundle(a -> 3, b -> 11) - * }}} - * Users can override the default behavior of `.toPrintable` in custom [[Bundle]] and [[Record]] - * types. - */ -// TODO Add support for names of Modules -// Currently impossible because unpack is called before the name is selected -// Could be implemented by adding a new format specifier to Firrtl (eg. %m) -// TODO Should we provide more functions like map and mkPrintable? -sealed abstract class Printable { - /** Unpack into format String and a List of String arguments (identifiers) - * @note This must be called after elaboration when Chisel nodes actually - * have names - */ - def unpack(ctx: Component): (String, Iterable[String]) - /** Allow for appending Printables like Strings */ - final def +(that: Printable): Printables = Printables(List(this, that)) - /** Allow for appending Strings to Printables */ - final def +(that: String): Printables = Printables(List(this, PString(that))) -} -object Printable { - /** Pack standard printf fmt, args* style into Printable - */ - def pack(fmt: String, data: Data*): Printable = { // scalastyle:ignore method.length - val args = data.toIterator - - // Error handling - def carrotAt(index: Int) = (" " * index) + "^" - def errorMsg(index: Int) = - s"""| fmt = "$fmt" - | ${carrotAt(index)} - | data = ${data mkString ", "}""".stripMargin - def getArg(i: Int): Data = { - if (!args.hasNext) { - val msg = "has no matching argument!\n" + errorMsg(i) - // Exception wraps msg in s"Format Specifier '$msg'" - throw new MissingFormatArgumentException(msg) - } - args.next() - } - - val pables = mutable.ListBuffer.empty[Printable] - var str = "" - var percent = false - for ((c, i) <- fmt.zipWithIndex) { - if (percent) { - val arg = c match { - case FirrtlFormat(x) => FirrtlFormat(x.toString, getArg(i)) - case 'n' => Name(getArg(i)) - case 'N' => FullName(getArg(i)) - case '%' => Percent - case x => - val msg = s"Illegal format specifier '$x'!\n" + errorMsg(i) - throw new UnknownFormatConversionException(msg) - } - pables += PString(str dropRight 1) // remove format % - pables += arg - str = "" - percent = false - } else { - str += c - percent = c == '%' - } - } - if (percent) { - val msg = s"Trailing %\n" + errorMsg(fmt.size - 1) - throw new UnknownFormatConversionException(msg) - } - require(!args.hasNext, - s"Too many arguments! More format specifier(s) expected!\n" + - errorMsg(fmt.size)) - - pables += PString(str) - Printables(pables) - } -} - -case class Printables(pables: Iterable[Printable]) extends Printable { - require(pables.hasDefiniteSize, "Infinite-sized iterables are not supported!") - final def unpack(ctx: Component): (String, Iterable[String]) = { - val (fmts, args) = pables.map(_ unpack ctx).unzip - (fmts.mkString, args.flatten) - } -} -/** Wrapper for printing Scala Strings */ -case class PString(str: String) extends Printable { - final def unpack(ctx: Component): (String, Iterable[String]) = - (str replaceAll ("%", "%%"), List.empty) -} -/** Superclass for Firrtl format specifiers for Bits */ -sealed abstract class FirrtlFormat(private[chisel3] val specifier: Char) extends Printable { - def bits: Bits - def unpack(ctx: Component): (String, Iterable[String]) = { - (s"%$specifier", List(bits.ref.fullName(ctx))) - } -} -object FirrtlFormat { - final val legalSpecifiers = List('d', 'x', 'b', 'c') - - def unapply(x: Char): Option[Char] = - Option(x) filter (x => legalSpecifiers contains x) - - /** Helper for constructing Firrtl Formats - * Accepts data to simplify pack - */ - def apply(specifier: String, data: Data): FirrtlFormat = { - val bits = data match { - case b: Bits => b - case d => throw new Exception(s"Trying to construct FirrtlFormat with non-bits $d!") - } - specifier match { - case "d" => Decimal(bits) - case "x" => Hexadecimal(bits) - case "b" => Binary(bits) - case "c" => Character(bits) - case c => throw new Exception(s"Illegal format specifier '$c'!") - } - } -} -/** Format bits as Decimal */ -case class Decimal(bits: Bits) extends FirrtlFormat('d') -/** Format bits as Hexidecimal */ -case class Hexadecimal(bits: Bits) extends FirrtlFormat('x') -/** Format bits as Binary */ -case class Binary(bits: Bits) extends FirrtlFormat('b') -/** Format bits as Character */ -case class Character(bits: Bits) extends FirrtlFormat('c') -/** Put innermost name (eg. field of bundle) */ -case class Name(data: Data) extends Printable { - final def unpack(ctx: Component): (String, Iterable[String]) = (data.ref.name, List.empty) -} -/** Put full name within parent namespace (eg. bundleName.field) */ -case class FullName(data: Data) extends Printable { - final def unpack(ctx: Component): (String, Iterable[String]) = (data.ref.fullName(ctx), List.empty) -} -/** Represents escaped percents */ -case object Percent extends Printable { - final def unpack(ctx: Component): (String, Iterable[String]) = ("%%", List.empty) -} diff --git a/chiselFrontend/src/main/scala/chisel3/Printf.scala b/chiselFrontend/src/main/scala/chisel3/Printf.scala deleted file mode 100644 index 0478e889..00000000 --- a/chiselFrontend/src/main/scala/chisel3/Printf.scala +++ /dev/null @@ -1,102 +0,0 @@ -// See LICENSE for license details. - -package chisel3 - -import scala.language.experimental.macros - -import chisel3.internal._ -import chisel3.internal.Builder.pushCommand -import chisel3.internal.firrtl._ -import chisel3.internal.sourceinfo.SourceInfo - -/** Prints a message in simulation - * - * See apply methods for use - */ -object printf { // scalastyle:ignore object.name - /** Helper for packing escape characters */ - private[chisel3] def format(formatIn: String): String = { - require(formatIn forall (c => c.toInt > 0 && c.toInt < 128), - "format strings must comprise non-null ASCII values") - def escaped(x: Char) = { - require(x.toInt >= 0, s"char ${x} to Int ${x.toInt} must be >= 0") - if (x == '"' || x == '\\') { - s"\\${x}" - } else if (x == '\n') { - "\\n" - } else if (x == '\t') { - "\\t" - } else { - require(x.toInt >= 32, s"char ${x} to Int ${x.toInt} must be >= 32") // TODO \xNN once FIRRTL issue #59 is resolved - x - } - } - formatIn map escaped mkString "" - } - - /** Prints a message in simulation - * - * Prints a message every cycle. If defined within the scope of a [[when]] block, the message - * will only be printed on cycles that the when condition is true. - * - * Does not fire when in reset (defined as the encapsulating Module's reset). If your definition - * of reset is not the encapsulating Module's reset, you will need to gate this externally. - * - * May be called outside of a Module (like defined in a function), uses the current default clock - * and reset. These can be overriden with [[withClockAndReset]]. - * - * ==Format Strings== - * - * This method expects a ''format string'' and an ''argument list'' in a similar style to printf - * in C. The format string expects a [[scala.Predef.String String]] that may contain ''format - * specifiers'' For example: - * {{{ - * printf("myWire has the value %d\n", myWire) - * }}} - * This prints the string "myWire has the value " followed by the current value of `myWire` (in - * decimal, followed by a newline. - * - * There must be exactly as many arguments as there are format specifiers - * - * ===Format Specifiers=== - * - * Format specifiers are prefixed by `%`. If you wish to print a literal `%`, use `%%`. - * - `%d` - Decimal - * - `%x` - Hexadecimal - * - `%b` - Binary - * - `%c` - 8-bit Character - * - `%n` - Name of a signal - * - `%N` - Full name of a leaf signal (in an aggregate) - * - * @param fmt printf format string - * @param data format string varargs containing data to print - */ - def apply(fmt: String, data: Bits*)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Unit = - apply(Printable.pack(fmt, data:_*)) - /** Prints a message in simulation - * - * Prints a message every cycle. If defined within the scope of a [[when]] block, the message - * will only be printed on cycles that the when condition is true. - * - * Does not fire when in reset (defined as the encapsulating Module's reset). If your definition - * of reset is not the encapsulating Module's reset, you will need to gate this externally. - * - * May be called outside of a Module (like defined in a function), uses the current default clock - * and reset. These can be overriden with [[withClockAndReset]]. - * - * @see [[Printable]] documentation - * @param pable [[Printable]] to print - */ - def apply(pable: Printable)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Unit = { - when (!Module.reset.asBool) { - printfWithoutReset(pable) - } - } - - private[chisel3] def printfWithoutReset(pable: Printable)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Unit = { // scalastyle:ignore line.size.limit - val clock = Builder.forcedClock - pushCommand(Printf(sourceInfo, clock.ref, pable)) - } - private[chisel3] def printfWithoutReset(fmt: String, data: Bits*)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Unit = // scalastyle:ignore line.size.limit - printfWithoutReset(Printable.pack(fmt, data:_*)) -} diff --git a/chiselFrontend/src/main/scala/chisel3/RawModule.scala b/chiselFrontend/src/main/scala/chisel3/RawModule.scala deleted file mode 100644 index 218022cc..00000000 --- a/chiselFrontend/src/main/scala/chisel3/RawModule.scala +++ /dev/null @@ -1,233 +0,0 @@ -// See LICENSE for license details. - -package chisel3 - -import scala.collection.mutable.{ArrayBuffer, HashMap} -import scala.collection.JavaConversions._ -import scala.language.experimental.macros - -import chisel3.experimental.BaseModule -import chisel3.internal._ -import chisel3.internal.Builder._ -import chisel3.internal.firrtl._ -import chisel3.internal.sourceinfo.UnlocatableSourceInfo - -/** Abstract base class for Modules that contain Chisel RTL. - * This abstract base class is a user-defined module which does not include implicit clock and reset and supports - * multiple IO() declarations. - */ -abstract class RawModule(implicit moduleCompileOptions: CompileOptions) - extends BaseModule { - // - // RTL construction internals - // - private val _commands = ArrayBuffer[Command]() - private[chisel3] def addCommand(c: Command) { - require(!_closed, "Can't write to module after module close") - _commands += c - } - protected def getCommands = { - require(_closed, "Can't get commands before module close") - _commands.toSeq - } - - // - // Other Internal Functions - // - // For debuggers/testers, TODO: refactor out into proper public API - private var _firrtlPorts: Option[Seq[firrtl.Port]] = None - lazy val getPorts = _firrtlPorts.get - - val compileOptions = moduleCompileOptions - - private[chisel3] def namePorts(names: HashMap[HasId, String]): Unit = { - for (port <- getModulePorts) { - port.suggestedName.orElse(names.get(port)) match { - case Some(name) => - if (_namespace.contains(name)) { - Builder.error(s"""Unable to name port $port to "$name" in $this,""" + - " name is already taken by another port!") - } - port.setRef(ModuleIO(this, _namespace.name(name))) - case None => - Builder.error(s"Unable to name port $port in $this, " + - "try making it a public field of the Module") - port.setRef(ModuleIO(this, "")) - } - } - } - - - private[chisel3] override def generateComponent(): Component = { // scalastyle:ignore cyclomatic.complexity - require(!_closed, "Can't generate module more than once") - _closed = true - - val names = nameIds(classOf[RawModule]) - - // Ports get first naming priority, since they are part of a Module's IO spec - namePorts(names) - - // Then everything else gets named - for ((node, name) <- names) { - node.suggestName(name) - } - - // All suggestions are in, force names to every node. - for (id <- getIds) { - id match { - case id: BaseModule => id.forceName(default=id.desiredName, _namespace) - case id: MemBase[_] => id.forceName(default="_T", _namespace) - case id: Data => - if (id.isSynthesizable) { - id.topBinding match { - case OpBinding(_) | MemoryPortBinding(_) | PortBinding(_) | RegBinding(_) | WireBinding(_) => - id.forceName(default="_T", _namespace) - case _ => // don't name literals - } - } // else, don't name unbound types - } - id._onModuleClose - } - - val firrtlPorts = getModulePorts map { port: Data => - // Special case Vec to make FIRRTL emit the direction of its - // element. - // Just taking the Vec's specifiedDirection is a bug in cases like - // Vec(Flipped()), since the Vec's specifiedDirection is - // Unspecified. - val direction = port match { - case v: Vec[_] => v.specifiedDirection match { - case SpecifiedDirection.Input => SpecifiedDirection.Input - case SpecifiedDirection.Output => SpecifiedDirection.Output - case SpecifiedDirection.Flip => SpecifiedDirection.flip(v.sample_element.specifiedDirection) - case SpecifiedDirection.Unspecified => v.sample_element.specifiedDirection - } - case _ => port.specifiedDirection - } - - Port(port, direction) - } - _firrtlPorts = Some(firrtlPorts) - - // Generate IO invalidation commands to initialize outputs as unused, - // unless the client wants explicit control over their generation. - val invalidateCommands = { - if (!compileOptions.explicitInvalidate) { - getModulePorts map { port => DefInvalid(UnlocatableSourceInfo, port.ref) } - } else { - Seq() - } - } - val component = DefModule(this, name, firrtlPorts, invalidateCommands ++ getCommands) - _component = Some(component) - component - } - - private[chisel3] def initializeInParent(parentCompileOptions: CompileOptions): Unit = { - implicit val sourceInfo = UnlocatableSourceInfo - - if (!parentCompileOptions.explicitInvalidate) { - for (port <- getModulePorts) { - pushCommand(DefInvalid(sourceInfo, port.ref)) - } - } - } -} - -trait RequireAsyncReset extends MultiIOModule { - override private[chisel3] def mkReset: AsyncReset = AsyncReset() -} - -trait RequireSyncReset extends MultiIOModule { - override private[chisel3] def mkReset: Bool = Bool() -} - -/** Abstract base class for Modules, which behave much like Verilog modules. - * These may contain both logic and state which are written in the Module - * body (constructor). - * This abstract base class includes an implicit clock and reset. - * - * @note Module instantiations must be wrapped in a Module() call. - */ -abstract class MultiIOModule(implicit moduleCompileOptions: CompileOptions) - extends RawModule { - // Implicit clock and reset pins - final val clock: Clock = IO(Input(Clock())) - final val reset: Reset = IO(Input(mkReset)) - - private[chisel3] def mkReset: Reset = { - // Top module and compatibility mode use Bool for reset - val inferReset = _parent.isDefined && moduleCompileOptions.inferModuleReset - if (inferReset) Reset() else Bool() - } - - // Setup ClockAndReset - Builder.currentClock = Some(clock) - Builder.currentReset = Some(reset) - - private[chisel3] override def initializeInParent(parentCompileOptions: CompileOptions): Unit = { - implicit val sourceInfo = UnlocatableSourceInfo - - super.initializeInParent(parentCompileOptions) - clock := Builder.forcedClock - reset := Builder.forcedReset - } -} - -package internal { - - /** Legacy Module class that restricts IOs to just io, clock, and reset, and provides a constructor - * for threading through explicit clock and reset. - * - * While this class isn't planned to be removed anytime soon (there are benefits to restricting - * IO), the clock and reset constructors will be phased out. Recommendation is to wrap the module - * in a withClock/withReset/withClockAndReset block, or directly hook up clock or reset IO pins. - */ - abstract class LegacyModule(implicit moduleCompileOptions: CompileOptions) - extends MultiIOModule { - // These are to be phased out - protected var override_clock: Option[Clock] = None - protected var override_reset: Option[Bool] = None - - // IO for this Module. At the Scala level (pre-FIRRTL transformations), - // connections in and out of a Module may only go through `io` elements. - def io: Record - - // Allow access to bindings from the compatibility package - protected def _compatIoPortBound() = portsContains(io)// scalastyle:ignore method.name - - private[chisel3] override def namePorts(names: HashMap[HasId, String]): Unit = { - for (port <- getModulePorts) { - // This should already have been caught - if (!names.contains(port)) throwException(s"Unable to name port $port in $this") - val name = names(port) - port.setRef(ModuleIO(this, _namespace.name(name))) - } - } - - private[chisel3] override def generateComponent(): Component = { - _compatAutoWrapPorts() // pre-IO(...) compatibility hack - - // Restrict IO to just io, clock, and reset - require(io != null, "Module must have io") - require(portsContains(io), "Module must have io wrapped in IO(...)") - require((portsContains(clock)) && (portsContains(reset)), "Internal error, module did not have clock or reset as IO") // scalastyle:ignore line.size.limit - require(portsSize == 3, "Module must only have io, clock, and reset as IO") - - super.generateComponent() - } - - private[chisel3] override def initializeInParent(parentCompileOptions: CompileOptions): Unit = { - // Don't generate source info referencing parents inside a module, since this interferes with - // module de-duplication in FIRRTL emission. - implicit val sourceInfo = UnlocatableSourceInfo - - if (!parentCompileOptions.explicitInvalidate) { - pushCommand(DefInvalid(sourceInfo, io.ref)) - } - - clock := override_clock.getOrElse(Builder.forcedClock) - reset := override_reset.getOrElse(Builder.forcedReset) - } - } -} diff --git a/chiselFrontend/src/main/scala/chisel3/Reg.scala b/chiselFrontend/src/main/scala/chisel3/Reg.scala deleted file mode 100644 index 7129c389..00000000 --- a/chiselFrontend/src/main/scala/chisel3/Reg.scala +++ /dev/null @@ -1,195 +0,0 @@ -// See LICENSE for license details. - -package chisel3 - -import scala.language.experimental.macros - -import chisel3.internal._ -import chisel3.internal.Builder.pushCommand -import chisel3.internal.firrtl._ -import chisel3.internal.sourceinfo.SourceInfo - -/** Utility for constructing hardware registers - * - * The width of a `Reg` (inferred or not) is copied from the type template - * {{{ - * val r0 = Reg(UInt()) // width is inferred - * val r1 = Reg(UInt(8.W)) // width is set to 8 - * - * val r2 = Reg(Vec(4, UInt())) // width is inferred - * val r3 = Reg(Vec(4, UInt(8.W))) // width of each element is set to 8 - * - * class MyBundle { - * val unknown = UInt() - * val known = UInt(8.W) - * } - * val r4 = Reg(new MyBundle) - * // Width of r4.unknown is inferred - * // Width of r4.known is set to 8 - * }}} - * - */ -object Reg { - /** Construct a [[Reg]] from a type template with no initialization value (reset is ignored). - * Value will not change unless the [[Reg]] is given a connection. - * @param t The template from which to construct this wire - */ - def apply[T <: Data](t: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = { - if (compileOptions.declaredTypeMustBeUnbound) { - requireIsChiselType(t, "reg type") - } - val reg = t.cloneTypeFull - val clock = Node(Builder.forcedClock) - - reg.bind(RegBinding(Builder.forcedUserModule)) - pushCommand(DefReg(sourceInfo, reg, clock)) - reg - } - -} - -/** Utility for constructing one-cycle delayed versions of signals - * - * ''The width of a `RegNext` is not set based on the `next` or `init` connections'' for [[Element]] types. In the - * following example, the width of `bar` will not be set and will be inferred by the FIRRTL compiler. - * {{{ - * val foo = Reg(UInt(4.W)) // width is 4 - * val bar = RegNext(foo) // width is unset - * }}} - * - * If you desire an explicit width, do not use `RegNext` and instead use a register with a specified width: - * {{{ - * val foo = Reg(UInt(4.W)) // width is 4 - * val bar = Reg(chiselTypeOf(foo)) // width is 4 - * bar := foo - * }}} - * - * Also note that a `RegNext` of a [[Bundle]] ''will have it's width set'' for [[Aggregate]] types. - * {{{ - * class MyBundle extends Bundle { - * val x = UInt(4.W) - * } - * val foo = Wire(new MyBundle) // the width of foo.x is 4 - * val bar = RegNext(foo) // the width of bar.x is 4 - * }}} - */ -object RegNext { - /** Returns a register ''with an unset width'' connected to the signal `next` and with no reset value. */ - def apply[T <: Data](next: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = { - val model = (next match { - case next: Bits => next.cloneTypeWidth(Width()) - case next => next.cloneTypeFull - }).asInstanceOf[T] - val reg = Reg(model) - - requireIsHardware(next, "reg next") - reg := next - - reg - } - - /** Returns a register ''with an unset width'' connected to the signal `next` and with the reset value `init`. */ - def apply[T <: Data](next: T, init: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = { - val model = (next match { - case next: Bits => next.cloneTypeWidth(Width()) - case next => next.cloneTypeFull - }).asInstanceOf[T] - val reg = RegInit(model, init) // TODO: this makes NO sense - - requireIsHardware(next, "reg next") - reg := next - - reg - } -} - -/** Utility for constructing hardware registers with an initialization value. - * - * The register is set to the initialization value when the current implicit `reset` is high - * - * The two forms of `RegInit` differ in how the type and width of the resulting [[Reg]] are - * specified. - * - * ==Single Argument== - * The single argument form uses the argument to specify both the type and reset value. For - * non-literal [[Bits]], the width of the [[Reg]] will be inferred. For literal [[Bits]] and all - * non-Bits arguments, the type will be copied from the argument. See the following examples for - * more details: - * - * 1. Literal [[Bits]] initializer: width will be set to match - * {{{ - * val r1 = RegInit(1.U) // width will be inferred to be 1 - * val r2 = RegInit(1.U(8.W)) // width is set to 8 - * }}} - * - * 2. Non-Literal [[Element]] initializer - width will be inferred - * {{{ - * val x = Wire(UInt()) - * val y = Wire(UInt(8.W)) - * val r1 = RegInit(x) // width will be inferred - * val r2 = RegInit(y) // width is set to 8 - * }}} - * - * 3. [[Aggregate]] initializer - width will be set to match the aggregate - * - * {{{ - * class MyBundle extends Bundle { - * val unknown = UInt() - * val known = UInt(8.W) - * } - * val w1 = Reg(new MyBundle) - * val w2 = RegInit(w1) - * // Width of w2.unknown is inferred - * // Width of w2.known is set to 8 - * }}} - * - * ==Double Argument== - * The double argument form allows the type of the [[Reg]] and the default connection to be - * specified independently. - * - * The width inference semantics for `RegInit` with two arguments match those of [[Reg]]. The - * first argument to `RegInit` is the type template which defines the width of the `Reg` in - * exactly the same way as the only argument to [[Wire]]. - * - * More explicitly, you can reason about `RegInit` with multiple arguments as if it were defined - * as: - * {{{ - * def RegInit[T <: Data](t: T, init: T): T = { - * val x = Reg(t) - * x := init - * x - * } - * }}} - */ -object RegInit { - /** Construct a [[Reg]] from a type template initialized to the specified value on reset - * @param t The type template used to construct this [[Reg]] - * @param init The value the [[Reg]] is initialized to on reset - */ - def apply[T <: Data](t: T, init: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = { - if (compileOptions.declaredTypeMustBeUnbound) { - requireIsChiselType(t, "reg type") - } - val reg = t.cloneTypeFull - val clock = Builder.forcedClock - val reset = Builder.forcedReset - - reg.bind(RegBinding(Builder.forcedUserModule)) - requireIsHardware(init, "reg initializer") - pushCommand(DefRegInit(sourceInfo, reg, clock.ref, reset.ref, init.ref)) - reg - } - - /** Construct a [[Reg]] initialized on reset to the specified value. - * @param init Initial value that serves as a type template and reset value - */ - def apply[T <: Data](init: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = { - val model = (init match { - // If init is a literal without forced width OR any non-literal, let width be inferred - case init: Bits if !init.litIsForcedWidth.getOrElse(false) => init.cloneTypeWidth(Width()) - case init => init.cloneTypeFull - }).asInstanceOf[T] - RegInit(model, init) - } - -} diff --git a/chiselFrontend/src/main/scala/chisel3/SIntFactory.scala b/chiselFrontend/src/main/scala/chisel3/SIntFactory.scala deleted file mode 100644 index c1c6b1db..00000000 --- a/chiselFrontend/src/main/scala/chisel3/SIntFactory.scala +++ /dev/null @@ -1,25 +0,0 @@ -// See LICENSE for license details. - -package chisel3 - -import chisel3.internal.firrtl.{IntervalRange, SLit, Width} - -trait SIntFactory { - /** Create an SInt type with inferred width. */ - def apply(): SInt = apply(Width()) - /** Create a SInt type or port with fixed width. */ - def apply(width: Width): SInt = new SInt(width) - - /** Create a SInt with the specified range */ - def apply(range: IntervalRange): SInt = { - apply(range.getWidth) - } - - /** Create an SInt literal with specified width. */ - // scalastyle:off method.name - protected[chisel3] def Lit(value: BigInt, width: Width): SInt = { - val lit = SLit(value, width) - val result = new SInt(lit.width) - lit.bindLitArg(result) - } -} diff --git a/chiselFrontend/src/main/scala/chisel3/SeqUtils.scala b/chiselFrontend/src/main/scala/chisel3/SeqUtils.scala deleted file mode 100644 index 28f753b1..00000000 --- a/chiselFrontend/src/main/scala/chisel3/SeqUtils.scala +++ /dev/null @@ -1,128 +0,0 @@ -// See LICENSE for license details. - -package chisel3 - -import chisel3.experimental.FixedPoint -import chisel3.internal.throwException - -import scala.language.experimental.macros -import chisel3.internal.sourceinfo._ - -//scalastyle:off method.name - -private[chisel3] object SeqUtils { - /** Concatenates the data elements of the input sequence, in sequence order, together. - * The first element of the sequence forms the least significant bits, while the last element - * in the sequence forms the most significant bits. - * - * Equivalent to r(n-1) ## ... ## r(1) ## r(0). - */ - def asUInt[T <: Bits](in: Seq[T]): UInt = macro SourceInfoTransform.inArg - - /** @group SourceInfoTransformMacros */ - def do_asUInt[T <: Bits](in: Seq[T])(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = { - if (in.tail.isEmpty) { - in.head.asUInt - } else { - val left = asUInt(in.slice(0, in.length/2)) - val right = asUInt(in.slice(in.length/2, in.length)) - right ## left - } - } - - /** Outputs the number of elements that === true.B. - */ - def count(in: Seq[Bool]): UInt = macro SourceInfoTransform.inArg - - /** @group SourceInfoTransformMacros */ - def do_count(in: Seq[Bool])(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = in.size match { - case 0 => 0.U - case 1 => in.head - case n => - val sum = count(in take n/2) +& count(in drop n/2) - sum(BigInt(n).bitLength - 1, 0) - } - - /** Returns the data value corresponding to the first true predicate. - */ - def priorityMux[T <: Data](in: Seq[(Bool, T)]): T = macro SourceInfoTransform.inArg - - /** @group SourceInfoTransformMacros */ - def do_priorityMux[T <: Data](in: Seq[(Bool, T)]) - (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = { - if (in.size == 1) { - in.head._2 - } else { - Mux(in.head._1, in.head._2, priorityMux(in.tail)) - } - } - - /** Returns the data value corresponding to the lone true predicate. - * This is elaborated to firrtl using a structure that should be optimized into and and/or tree. - * - * @note assumes exactly one true predicate, results undefined otherwise - * FixedPoint values or aggregates containing FixedPoint values cause this optimized structure to be lost - */ - def oneHotMux[T <: Data](in: Iterable[(Bool, T)]): T = macro SourceInfoTransform.inArg - - //scalastyle:off method.length cyclomatic.complexity - /** @group SourceInfoTransformMacros */ - def do_oneHotMux[T <: Data](in: Iterable[(Bool, T)]) - (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = { - if (in.tail.isEmpty) { - in.head._2 - } - else { - val output = cloneSupertype(in.toSeq map { _._2}, "oneHotMux") - - def buildAndOrMultiplexor[TT <: Data](inputs: Iterable[(Bool, TT)]): T = { - val masked = for ((s, i) <- inputs) yield Mux(s, i.asUInt(), 0.U) - masked.reduceLeft(_ | _).asTypeOf(output) - } - - output match { - case _: SInt => - // SInt's have to be managed carefully so sign extension works - - val sInts: Iterable[(Bool, SInt)] = in.collect { case (s: Bool, f: SInt) => - (s, f.asTypeOf(output).asInstanceOf[SInt]) - } - - val masked = for ((s, i) <- sInts) yield Mux(s, i, 0.S) - masked.reduceLeft(_ | _).asTypeOf(output) - - case _: FixedPoint => - val (sels, possibleOuts) = in.toSeq.unzip - - val (intWidths, binaryPoints) = in.toSeq.map { case (_, o) => - val fo = o.asInstanceOf[FixedPoint] - require(fo.binaryPoint.known, "Mux1H requires width/binary points to be defined") - (fo.getWidth - fo.binaryPoint.get, fo.binaryPoint.get) - }.unzip - - if (intWidths.distinct.length == 1 && binaryPoints.distinct.length == 1) { - buildAndOrMultiplexor(in) - } - else { - val maxIntWidth = intWidths.max - val maxBP = binaryPoints.max - val inWidthMatched = Seq.fill(intWidths.length)(Wire(FixedPoint((maxIntWidth + maxBP).W, maxBP.BP))) - inWidthMatched.zipWithIndex foreach { case (e, idx) => e := possibleOuts(idx).asInstanceOf[FixedPoint] } - buildAndOrMultiplexor(sels.zip(inWidthMatched)) - } - - case _: Aggregate => - val allDefineWidth = in.forall { case (_, element) => element.widthOption.isDefined } - if(allDefineWidth) { - buildAndOrMultiplexor(in) - } - else { - throwException(s"Cannot Mux1H with aggregates with inferred widths") - } - - case _ => - buildAndOrMultiplexor(in) - } - } - } -} diff --git a/chiselFrontend/src/main/scala/chisel3/StrongEnum.scala b/chiselFrontend/src/main/scala/chisel3/StrongEnum.scala deleted file mode 100644 index 8edce4d8..00000000 --- a/chiselFrontend/src/main/scala/chisel3/StrongEnum.scala +++ /dev/null @@ -1,343 +0,0 @@ -// See LICENSE for license details. - -package chisel3.experimental - -import scala.language.experimental.macros -import scala.reflect.macros.blackbox.Context -import scala.collection.mutable -import chisel3._ -import chisel3.internal.Builder.pushOp -import chisel3.internal.firrtl.PrimOp._ -import chisel3.internal.firrtl._ -import chisel3.internal.sourceinfo._ -import chisel3.internal.{Binding, Builder, ChildBinding, ConstrainedBinding, InstanceId, throwException} -import firrtl.annotations._ - - -object EnumAnnotations { - /** An annotation for strong enum instances that are ''not'' inside of Vecs - * - * @param target the enum instance being annotated - * @param typeName the name of the enum's type (e.g. ''"mypackage.MyEnum"'') - */ - case class EnumComponentAnnotation(target: Named, enumTypeName: String) extends SingleTargetAnnotation[Named] { - def duplicate(n: Named): EnumComponentAnnotation = this.copy(target = n) - } - - case class EnumComponentChiselAnnotation(target: InstanceId, enumTypeName: String) extends ChiselAnnotation { - def toFirrtl: EnumComponentAnnotation = EnumComponentAnnotation(target.toNamed, enumTypeName) - } - - /** An annotation for Vecs of strong enums. - * - * The ''fields'' parameter deserves special attention, since it may be difficult to understand. Suppose you create a the following Vec: - - * {{{ - * VecInit(new Bundle { - * val e = MyEnum() - * val b = new Bundle { - * val inner_e = MyEnum() - * } - * val v = Vec(3, MyEnum()) - * } - * }}} - * - * Then, the ''fields'' parameter will be: ''Seq(Seq("e"), Seq("b", "inner_e"), Seq("v"))''. Note that for any Vec that doesn't contain Bundles, this field will simply be an empty Seq. - * - * @param target the Vec being annotated - * @param typeName the name of the enum's type (e.g. ''"mypackage.MyEnum"'') - * @param fields a list of all chains of elements leading from the Vec instance to its inner enum fields. - * - */ - case class EnumVecAnnotation(target: Named, typeName: String, fields: Seq[Seq[String]]) extends SingleTargetAnnotation[Named] { - def duplicate(n: Named) = this.copy(target = n) - } - - case class EnumVecChiselAnnotation(target: InstanceId, typeName: String, fields: Seq[Seq[String]]) extends ChiselAnnotation { - override def toFirrtl = EnumVecAnnotation(target.toNamed, typeName, fields) - } - - /** An annotation for enum types (rather than enum ''instances''). - * - * @param typeName the name of the enum's type (e.g. ''"mypackage.MyEnum"'') - * @param definition a map describing which integer values correspond to which enum names - */ - case class EnumDefAnnotation(typeName: String, definition: Map[String, BigInt]) extends NoTargetAnnotation - - case class EnumDefChiselAnnotation(typeName: String, definition: Map[String, BigInt]) extends ChiselAnnotation { - override def toFirrtl: Annotation = EnumDefAnnotation(typeName, definition) - } -} -import EnumAnnotations._ - - -abstract class EnumType(private val factory: EnumFactory, selfAnnotating: Boolean = true) extends Element { - override def toString: String = { - val bindingString = litOption match { - case Some(value) => factory.nameOfValue(value) match { - case Some(name) => s"($value=$name)" - case None => s"($value=(invalid))" - } - case _ => bindingToString - } - // Use getSimpleName instead of enumTypeName because for debugging purposes the fully qualified name isn't - // necessary (compared to for the Enum annotation), and it's more consistent with Bundle printing. - s"${factory.getClass.getSimpleName.init}$bindingString" - } - - override def cloneType: this.type = factory().asInstanceOf[this.type] - - private[chisel3] def compop(sourceInfo: SourceInfo, op: PrimOp, other: EnumType): Bool = { - requireIsHardware(this, "bits operated on") - requireIsHardware(other, "bits operated on") - - if(!this.typeEquivalent(other)) { - throwException(s"Enum types are not equivalent: ${this.enumTypeName}, ${other.enumTypeName}") - } - - pushOp(DefPrim(sourceInfo, Bool(), op, this.ref, other.ref)) - } - - private[chisel3] override def typeEquivalent(that: Data): Boolean = { - this.getClass == that.getClass && - this.factory == that.asInstanceOf[EnumType].factory - } - - private[chisel3] override def connectFromBits(that: Bits)(implicit sourceInfo: SourceInfo, - compileOptions: CompileOptions): Unit = { - this := factory.apply(that.asUInt) - } - - final def === (that: EnumType): Bool = macro SourceInfoTransform.thatArg - final def =/= (that: EnumType): Bool = macro SourceInfoTransform.thatArg - final def < (that: EnumType): Bool = macro SourceInfoTransform.thatArg - final def <= (that: EnumType): Bool = macro SourceInfoTransform.thatArg - final def > (that: EnumType): Bool = macro SourceInfoTransform.thatArg - final def >= (that: EnumType): Bool = macro SourceInfoTransform.thatArg - - // scalastyle:off line.size.limit method.name - def do_=== (that: EnumType)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, EqualOp, that) - def do_=/= (that: EnumType)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, NotEqualOp, that) - def do_< (that: EnumType)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, LessOp, that) - def do_> (that: EnumType)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, GreaterOp, that) - def do_<= (that: EnumType)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, LessEqOp, that) - def do_>= (that: EnumType)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, GreaterEqOp, that) - // scalastyle:on line.size.limit method.name - - override def do_asUInt(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = - pushOp(DefPrim(sourceInfo, UInt(width), AsUIntOp, ref)) - - protected[chisel3] override def width: Width = factory.width - - def isValid(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = { - if (litOption.isDefined) { - true.B - } else { - factory.all.map(this === _).reduce(_ || _) - } - } - - def next(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): this.type = { - if (litOption.isDefined) { - val index = factory.all.indexOf(this) - - if (index < factory.all.length-1) { - factory.all(index + 1).asInstanceOf[this.type] - } else { - factory.all.head.asInstanceOf[this.type] - } - } else { - val enums_with_nexts = factory.all zip (factory.all.tail :+ factory.all.head) - val next_enum = SeqUtils.priorityMux(enums_with_nexts.map { case (e,n) => (this === e, n) } ) - next_enum.asInstanceOf[this.type] - } - } - - private[chisel3] def bindToLiteral(num: BigInt, w: Width): Unit = { - val lit = ULit(num, w) - lit.bindLitArg(this) - } - - override private[chisel3] def bind(target: Binding, parentDirection: SpecifiedDirection = SpecifiedDirection.Unspecified): Unit = { - super.bind(target, parentDirection) - - // Make sure we only annotate hardware and not literals - if (selfAnnotating && isSynthesizable && topBindingOpt.get.isInstanceOf[ConstrainedBinding]) { - annotateEnum() - } - } - - // This function conducts a depth-wise search to find all enum-type fields within a vector or bundle (or vector of bundles) - private def enumFields(d: Aggregate): Seq[Seq[String]] = d match { - case v: Vec[_] => v.sample_element match { - case b: Bundle => enumFields (b) - case _ => Seq () - } - case b: Bundle => - b.elements.collect { - case (name, e: EnumType) if this.typeEquivalent(e) => Seq(Seq(name)) - case (name, v: Vec[_]) if this.typeEquivalent(v.sample_element) => Seq(Seq(name)) - case (name, b2: Bundle) => enumFields(b2).map(name +: _) - }.flatten.toSeq - } - - private def outerMostVec(d: Data = this): Option[Vec[_]] = { - val currentVecOpt = d match { - case v: Vec[_] => Some(v) - case _ => None - } - - d.binding match { - case Some(ChildBinding(parent)) => outerMostVec(parent) match { - case outer @ Some(_) => outer - case None => currentVecOpt - } - case _ => currentVecOpt - } - } - - private def annotateEnum(): Unit = { - val anno = outerMostVec() match { - case Some(v) => EnumVecChiselAnnotation(v, enumTypeName, enumFields(v)) - case None => EnumComponentChiselAnnotation(this, enumTypeName) - } - - if (!Builder.annotations.contains(anno)) { - annotate(anno) - } - - if (!Builder.annotations.contains(factory.globalAnnotation)) { - annotate(factory.globalAnnotation) - } - } - - protected def enumTypeName: String = factory.enumTypeName - - def toPrintable: Printable = FullName(this) // TODO: Find a better pretty printer -} - - -abstract class EnumFactory { - class Type extends EnumType(this) - object Type { - def apply(): Type = EnumFactory.this.apply() - } - - private var id: BigInt = 0 - private[chisel3] var width: Width = 0.W - - private case class EnumRecord(inst: Type, name: String) - private val enum_records = mutable.ArrayBuffer.empty[EnumRecord] - - private def enumNames = enum_records.map(_.name).toSeq - private def enumValues = enum_records.map(_.inst.litValue()).toSeq - private def enumInstances = enum_records.map(_.inst).toSeq - - private[chisel3] val enumTypeName = getClass.getName.init - - private[chisel3] def globalAnnotation: EnumDefChiselAnnotation = - EnumDefChiselAnnotation(enumTypeName, (enumNames, enumValues).zipped.toMap) - - def getWidth: Int = width.get - - def all: Seq[Type] = enumInstances - - private[chisel3] def nameOfValue(id: BigInt): Option[String] = { - enum_records.find(_.inst.litValue() == id).map(_.name) - } - - protected def Value: Type = macro EnumMacros.ValImpl // scalastyle:off method.name - protected def Value(id: UInt): Type = macro EnumMacros.ValCustomImpl // scalastyle:off method.name - - protected def do_Value(name: String): Type = { - val result = new Type - - // We have to use UnknownWidth here, because we don't actually know what the final width will be - result.bindToLiteral(id, UnknownWidth()) - - enum_records.append(EnumRecord(result, name)) - - width = (1 max id.bitLength).W - id += 1 - - result - } - - protected def do_Value(name: String, id: UInt): Type = { - // TODO: These throw ExceptionInInitializerError which can be confusing to the user. Get rid of the error, and just - // throw an exception - if (id.litOption.isEmpty) { - throwException(s"$enumTypeName defined with a non-literal type") - } - if (id.litValue() < this.id) { - throwException(s"Enums must be strictly increasing: $enumTypeName") - } - - this.id = id.litValue() - do_Value(name) - } - - def apply(): Type = new Type - - def apply(n: UInt)(implicit sourceInfo: SourceInfo, connectionCompileOptions: CompileOptions): Type = { - // scalastyle:off line.size.limit - if (n.litOption.isDefined) { - enumInstances.find(_.litValue == n.litValue) match { - case Some(result) => result - case None => throwException(s"${n.litValue} is not a valid value for $enumTypeName") - } - } else if (!n.isWidthKnown) { - throwException(s"Non-literal UInts being cast to $enumTypeName must have a defined width") - } else if (n.getWidth > this.getWidth) { - throwException(s"The UInt being cast to $enumTypeName is wider than $enumTypeName's width ($getWidth)") - } else { - Builder.warning(s"Casting non-literal UInt to $enumTypeName. You can check that its value is legal by calling isValid") - - val glue = Wire(new UnsafeEnum(width)) - glue := n - val result = Wire(new Type) - result := glue - result - } - } - // scalastyle:on line.size.limit -} - - -private[chisel3] object EnumMacros { - def ValImpl(c: Context) : c.Tree = { // scalastyle:off method.name - import c.universe._ - - // Much thanks to michael_s for this solution: - // stackoverflow.com/questions/18450203/retrieve-the-name-of-the-value-a-scala-macro-invocation-will-be-assigned-to - val term = c.internal.enclosingOwner - val name = term.name.decodedName.toString.trim - - if (name.contains(" ")) { - c.abort(c.enclosingPosition, "Value cannot be called without assigning to an enum") - } - - q"""this.do_Value($name)""" - } - - def ValCustomImpl(c: Context)(id: c.Expr[UInt]): c.universe.Tree = { // scalastyle:off method.name - import c.universe._ - - val term = c.internal.enclosingOwner - val name = term.name.decodedName.toString.trim - - if (name.contains(" ")) { - c.abort(c.enclosingPosition, "Value cannot be called without assigning to an enum") - } - - q"""this.do_Value($name, $id)""" - } -} - - -// This is an enum type that can be connected directly to UInts. It is used as a "glue" to cast non-literal UInts -// to enums. -private[chisel3] class UnsafeEnum(override val width: Width) extends EnumType(UnsafeEnum, selfAnnotating = false) { - override def cloneType: this.type = new UnsafeEnum(width).asInstanceOf[this.type] -} -private object UnsafeEnum extends EnumFactory diff --git a/chiselFrontend/src/main/scala/chisel3/UIntFactory.scala b/chiselFrontend/src/main/scala/chisel3/UIntFactory.scala deleted file mode 100644 index 3868962b..00000000 --- a/chiselFrontend/src/main/scala/chisel3/UIntFactory.scala +++ /dev/null @@ -1,47 +0,0 @@ -// See LICENSE for license details. - -package chisel3 - -import chisel3.internal.firrtl.{IntervalRange, KnownWidth, ULit, UnknownWidth, Width} -import firrtl.Utils -import firrtl.constraint.IsKnown -import firrtl.ir.{Closed, IntWidth, Open} - -// This is currently a factory because both Bits and UInt inherit it. -trait UIntFactory { - /** Create a UInt type with inferred width. */ - def apply(): UInt = apply(Width()) - /** Create a UInt port with specified width. */ - def apply(width: Width): UInt = new UInt(width) - - /** Create a UInt literal with specified width. */ - // scalastyle:off method.name - protected[chisel3] def Lit(value: BigInt, width: Width): UInt = { - val lit = ULit(value, width) - val result = new UInt(lit.width) - // Bind result to being an Literal - lit.bindLitArg(result) - } - /** Create a UInt with the specified range, validate that range is effectively > 0 - */ - //scalastyle:off cyclomatic.complexity - def apply(range: IntervalRange): UInt = { - // Check is only done against lower bound because range will already insist that range high >= low - range.lowerBound match { - case Closed(bound) if bound < 0 => - throw new ChiselException(s"Attempt to create UInt with closed lower bound of $bound, must be > 0") - case Open(bound) if bound < -1 => - throw new ChiselException(s"Attempt to create UInt with open lower bound of $bound, must be > -1") - case _ => - } - - // because this is a UInt we don't have to take into account the lower bound - val newWidth = if(range.upperBound.isInstanceOf[IsKnown]) { - KnownWidth(Utils.getUIntWidth(range.maxAdjusted.get).max(1)) // max(1) handles range"[0,0]" - } else { - UnknownWidth() - } - - apply(newWidth) - } -} diff --git a/chiselFrontend/src/main/scala/chisel3/When.scala b/chiselFrontend/src/main/scala/chisel3/When.scala deleted file mode 100644 index ea243bbe..00000000 --- a/chiselFrontend/src/main/scala/chisel3/When.scala +++ /dev/null @@ -1,85 +0,0 @@ -// See LICENSE for license details. - -package chisel3 - -import scala.language.experimental.macros - -import chisel3.internal._ -import chisel3.internal.Builder.pushCommand -import chisel3.internal.firrtl._ -import chisel3.internal.sourceinfo.{SourceInfo} - -object when { // scalastyle:ignore object.name - /** Create a `when` condition block, where whether a block of logic is - * executed or not depends on the conditional. - * - * @param cond condition to execute upon - * @param block logic that runs only if `cond` is true - * - * @example - * {{{ - * when ( myData === 3.U ) { - * // Some logic to run when myData equals 3. - * } .elsewhen ( myData === 1.U ) { - * // Some logic to run when myData equals 1. - * } .otherwise { - * // Some logic to run when myData is neither 3 nor 1. - * } - * }}} - */ - - def apply(cond: => Bool)(block: => Any)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): WhenContext = { // scalastyle:ignore line.size.limit - new WhenContext(sourceInfo, Some(() => cond), block) - } -} - -/** A WhenContext may represent a when, and elsewhen, or an - * otherwise. Since FIRRTL does not have an "elsif" statement, - * alternatives must be mapped to nested if-else statements inside - * the alternatives of the preceeding condition. In order to emit - * proper FIRRTL, it is necessary to keep track of the depth of - * nesting of the FIRRTL whens. Due to the "thin frontend" nature of - * Chisel3, it is not possible to know if a when or elsewhen has a - * succeeding elsewhen or otherwise; therefore, this information is - * added by preprocessing the command queue. - */ -final class WhenContext(sourceInfo: SourceInfo, cond: Option[() => Bool], block: => Any, firrtlDepth: Int = 0) { - - /** This block of logic gets executed if above conditions have been - * false and this condition is true. The lazy argument pattern - * makes it possible to delay evaluation of cond, emitting the - * declaration and assignment of the Bool node of the predicate in - * the correct place. - */ - def elsewhen (elseCond: => Bool)(block: => Any)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): WhenContext = { // scalastyle:ignore line.size.limit - new WhenContext(sourceInfo, Some(() => elseCond), block, firrtlDepth + 1) - } - - /** This block of logic gets executed only if the above conditions - * were all false. No additional logic blocks may be appended past - * the `otherwise`. The lazy argument pattern makes it possible to - * delay evaluation of cond, emitting the declaration and - * assignment of the Bool node of the predicate in the correct - * place. - */ - def otherwise(block: => Any)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Unit = - new WhenContext(sourceInfo, None, block, firrtlDepth + 1) - - /* - * - */ - if (firrtlDepth > 0) { pushCommand(AltBegin(sourceInfo)) } - cond.foreach( c => pushCommand(WhenBegin(sourceInfo, c().ref)) ) - Builder.whenDepth += 1 - try { - block - } catch { - case ret: scala.runtime.NonLocalReturnControl[_] => - throwException("Cannot exit from a when() block with a \"return\"!" + - " Perhaps you meant to use Mux or a Wire as a return value?" - ) - } - Builder.whenDepth -= 1 - cond.foreach( c => pushCommand(WhenEnd(sourceInfo,firrtlDepth)) ) - if (cond.isEmpty) { pushCommand(OtherwiseEnd(sourceInfo,firrtlDepth)) } -} diff --git a/chiselFrontend/src/main/scala/chisel3/aop/Aspect.scala b/chiselFrontend/src/main/scala/chisel3/aop/Aspect.scala deleted file mode 100644 index 9f10a0dd..00000000 --- a/chiselFrontend/src/main/scala/chisel3/aop/Aspect.scala +++ /dev/null @@ -1,40 +0,0 @@ -// See LICENSE for license details. - -package chisel3.aop - -import chisel3.RawModule -import firrtl.annotations.{Annotation, NoTargetAnnotation} -import firrtl.options.Unserializable -import firrtl.AnnotationSeq - -/** Represents an aspect of a Chisel module, by specifying - * what behavior should be done to instance, via the FIRRTL Annotation Mechanism - * @tparam T Type of top-level module - */ -abstract class Aspect[T <: RawModule] extends Annotation with Unserializable with NoTargetAnnotation { - /** Convert this Aspect to a seq of FIRRTL annotation - * @param top - * @return - */ - def toAnnotation(top: T): AnnotationSeq - - /** Called by [[chisel3.stage.phases.AspectPhase]] to resolve this Aspect into annotations - * @param top - * @return - */ - private[chisel3] def resolveAspect(top: RawModule): AnnotationSeq = { - toAnnotation(top.asInstanceOf[T]) - } -} - -/** Holds utility functions for Aspect stuff */ -object Aspect { - - /** Converts elaborated Chisel components to FIRRTL modules - * @param chiselIR - * @return - */ - def getFirrtl(chiselIR: chisel3.internal.firrtl.Circuit): firrtl.ir.Circuit = { - chisel3.internal.firrtl.Converter.convert(chiselIR) - } -} diff --git a/chiselFrontend/src/main/scala/chisel3/core/package.scala b/chiselFrontend/src/main/scala/chisel3/core/package.scala deleted file mode 100644 index 92c4617b..00000000 --- a/chiselFrontend/src/main/scala/chisel3/core/package.scala +++ /dev/null @@ -1,288 +0,0 @@ -// See LICENSE for license details. - -package chisel3 - -/** - * These definitions exist to deal with those clients that relied on chisel3.core. - * They are deprecated and will be removed in the future. - */ -package object core { - - @deprecated("Use the version in chisel3._", "3.2") - val CompileOptions = chisel3.CompileOptions - - @deprecated("Use the version in chisel3._", "3.2") - val Input = chisel3.Input - @deprecated("Use the version in chisel3._", "3.2") - val Output = chisel3.Output - @deprecated("Use the version in chisel3._", "3.2") - val Flipped = chisel3.Flipped - @deprecated("Use the version in chisel3._", "3.2") - val chiselTypeOf = chisel3.chiselTypeOf - - @deprecated("Use the version in chisel3._", "3.2") - type Data = chisel3.Data - - @deprecated("Use the version in chisel3._", "3.2") - val WireDefault = chisel3.WireDefault - - @deprecated("Use the version in chisel3._", "3.2") - val Clock = chisel3.Clock - @deprecated("Use the version in chisel3._", "3.2") - type Clock = chisel3.Clock - - @deprecated("Use the version in chisel3._", "3.2") - type Reset = chisel3.Reset - - @deprecated("Use the version in chisel3._", "3.2") - type Aggregate = chisel3.Aggregate - - @deprecated("Use the version in chisel3._", "3.2") - val Vec = chisel3.Vec - @deprecated("Use the version in chisel3._", "3.2") - val VecInit = chisel3.VecInit - @deprecated("Use the version in chisel3._", "3.2") - type Vec[T <: Data] = chisel3.Vec[T] - @deprecated("Use the version in chisel3._", "3.2") - type VecLike[T <: Data] = chisel3.VecLike[T] - @deprecated("Use the version in chisel3._", "3.2") - type Bundle = chisel3.Bundle - @deprecated("Use the version in chisel3._", "3.2") - type IgnoreSeqInBundle = chisel3.IgnoreSeqInBundle - @deprecated("Use the version in chisel3._", "3.2") - type Record = chisel3.Record - - @deprecated("Use the version in chisel3._", "3.2") - val assert = chisel3.assert - - @deprecated("Use the version in chisel3._", "3.2") - type Element = chisel3.Element - @deprecated("Use the version in chisel3._", "3.2") - type Bits = chisel3.Bits - - // These provide temporary compatibility for those who foolishly imported from chisel3.core - @deprecated("Avoid importing from chisel3.core, these are not public APIs and may change at any time. " + - " Use chisel3.RawModule instead. This alias will be removed in 3.3.", "since the beginning of time") - type RawModule = chisel3.RawModule - @deprecated("Avoid importing from chisel3.core, these are not public APIs and may change at any time. " + - "Use chisel3.MultiIOModule instead. This alias will be removed in 3.3.", "since the beginning of time") - type MultiIOModule = chisel3.MultiIOModule - @deprecated("Avoid importing from chisel3.core, these are not public APIs and may change at any time. " + - " Use chisel3.RawModule instead. This alias will be removed in 3.3.", "since the beginning of time") - type UserModule = chisel3.RawModule - @deprecated("Avoid importing from chisel3.core, these are not public APIs and may change at any time. " + - "Use chisel3.MultiIOModule instead. This alias will be removed in 3.3.", "since the beginning of time") - type ImplicitModule = chisel3.MultiIOModule - - @deprecated("Use the version in chisel3._", "3.2") - val Bits = chisel3.Bits - @deprecated("Use the version in chisel3._", "3.2") - type Num[T <: chisel3.Data] = chisel3.Num[T] - @deprecated("Use the version in chisel3._", "3.2") - type UInt = chisel3.UInt - @deprecated("Use the version in chisel3._", "3.2") - val UInt = chisel3.UInt - @deprecated("Use the version in chisel3._", "3.2") - type SInt = chisel3.SInt - @deprecated("Use the version in chisel3._", "3.2") - val SInt = chisel3.SInt - @deprecated("Use the version in chisel3._", "3.2") - type Bool = chisel3.Bool - @deprecated("Use the version in chisel3._", "3.2") - val Bool = chisel3.Bool - @deprecated("Use the version in chisel3._", "3.2") - val Mux = chisel3.Mux - - @deprecated("Use the version in chisel3._", "3.2") - type BlackBox = chisel3.BlackBox - - @deprecated("Use the version in chisel3._", "3.2") - val Mem = chisel3.Mem - @deprecated("Use the version in chisel3._", "3.2") - type MemBase[T <: chisel3.Data] = chisel3.MemBase[T] - @deprecated("Use the version in chisel3._", "3.2") - type Mem[T <: chisel3.Data] = chisel3.Mem[T] - @deprecated("Use the version in chisel3._", "3.2") - val SyncReadMem = chisel3.SyncReadMem - @deprecated("Use the version in chisel3._", "3.2") - type SyncReadMem[T <: chisel3.Data] = chisel3.SyncReadMem[T] - - @deprecated("Use the version in chisel3._", "3.2") - val Module = chisel3.Module - @deprecated("Use the version in chisel3._", "3.2") - type Module = chisel3.Module - - @deprecated("Use the version in chisel3._", "3.2") - val printf = chisel3.printf - - @deprecated("Use the version in chisel3._", "3.2") - val RegNext = chisel3.RegNext - @deprecated("Use the version in chisel3._", "3.2") - val RegInit = chisel3.RegInit - @deprecated("Use the version in chisel3._", "3.2") - val Reg = chisel3.Reg - - @deprecated("Use the version in chisel3._", "3.2") - val when = chisel3.when - @deprecated("Use the version in chisel3._", "3.2") - type WhenContext = chisel3.WhenContext - - @deprecated("Use the version in chisel3._", "3.2") - type Printable = chisel3.Printable - @deprecated("Use the version in chisel3._", "3.2") - val Printable = chisel3.Printable - @deprecated("Use the version in chisel3._", "3.2") - type Printables = chisel3.Printables - @deprecated("Use the version in chisel3._", "3.2") - val Printables = chisel3.Printables - @deprecated("Use the version in chisel3._", "3.2") - type PString = chisel3.PString - @deprecated("Use the version in chisel3._", "3.2") - val PString = chisel3.PString - @deprecated("Use the version in chisel3._", "3.2") - type FirrtlFormat = chisel3.FirrtlFormat - @deprecated("Use the version in chisel3._", "3.2") - val FirrtlFormat = chisel3.FirrtlFormat - @deprecated("Use the version in chisel3._", "3.2") - type Decimal = chisel3.Decimal - @deprecated("Use the version in chisel3._", "3.2") - val Decimal = chisel3.Decimal - @deprecated("Use the version in chisel3._", "3.2") - type Hexadecimal = chisel3.Hexadecimal - val Hexadecimal = chisel3.Hexadecimal - @deprecated("Use the version in chisel3._", "3.2") - type Binary = chisel3.Binary - @deprecated("Use the version in chisel3._", "3.2") - val Binary = chisel3.Binary - @deprecated("Use the version in chisel3._", "3.2") - type Character = chisel3.Character - @deprecated("Use the version in chisel3._", "3.2") - val Character = chisel3.Character - @deprecated("Use the version in chisel3._", "3.2") - type Name = chisel3.Name - @deprecated("Use the version in chisel3._", "3.2") - val Name = chisel3.Name - @deprecated("Use the version in chisel3._", "3.2") - type FullName = chisel3.FullName - @deprecated("Use the version in chisel3._", "3.2") - val FullName = chisel3.FullName - @deprecated("Use the version in chisel3._", "3.2") - val Percent = chisel3.Percent - - @deprecated("Use the version in chisel3.experimental._", "3.2") - type Param = chisel3.experimental.Param - @deprecated("Use the version in chisel3.experimental._", "3.2") - type IntParam = chisel3.experimental.IntParam - @deprecated("Use the version in chisel3.experimental._", "3.2") - val IntParam = chisel3.experimental.IntParam - @deprecated("Use the version in chisel3.experimental._", "3.2") - type DoubleParam = chisel3.experimental.DoubleParam - @deprecated("Use the version in chisel3.experimental._", "3.2") - val DoubleParam = chisel3.experimental.DoubleParam - @deprecated("Use the version in chisel3.experimental._", "3.2") - type StringParam = chisel3.experimental.StringParam - @deprecated("Use the version in chisel3.experimental._", "3.2") - val StringParam = chisel3.experimental.StringParam - @deprecated("Use the version in chisel3.experimental._", "3.2") - type RawParam = chisel3.experimental.RawParam - @deprecated("Use the version in chisel3.experimental._", "3.2") - val RawParam = chisel3.experimental.RawParam - - @deprecated("Use the version in chisel3.experimental._", "3.2") - type Analog = chisel3.experimental.Analog - @deprecated("Use the version in chisel3.experimental._", "3.2") - val Analog = chisel3.experimental.Analog - - @deprecated("Use the version in chisel3._", "3.2") - implicit class fromIntToWidth(int: Int) extends chisel3.fromIntToWidth(int) - - @deprecated("Use the version in chisel3.experimental._", "3.2") - val attach = chisel3.experimental.attach - - @deprecated("Use the version in chisel3.experimental._", "3.2") - type EnumType = chisel3.experimental.EnumType - @deprecated("Use the version in chisel3.experimental._", "3.2") - type EnumFactory = chisel3.experimental.EnumFactory - @deprecated("Use the version in chisel3.experimental._", "3.2") - val EnumAnnotations = chisel3.experimental.EnumAnnotations - - @deprecated("Use the version in chisel3._", "3.2") - val withClockAndReset = chisel3.withClockAndReset - @deprecated("Use the version in chisel3._", "3.2") - val withClock = chisel3.withClock - @deprecated("Use the version in chisel3._", "3.2") - val withReset = chisel3.withReset - - @deprecated("Use the version in chisel3._", "3.2") - val dontTouch = chisel3.dontTouch - - @deprecated("Use the version in chisel3.experimental._", "3.2") - type BaseModule = chisel3.experimental.BaseModule - @deprecated("Use the version in chisel3.experimental._", "3.2") - type ExtModule = chisel3.experimental.ExtModule - - @deprecated("Use the version in chisel3.experimental._", "3.2") - val IO = chisel3.experimental.IO - - @deprecated("Use the version in chisel3.experimental._", "3.2") - type FixedPoint = chisel3.experimental.FixedPoint - @deprecated("Use the version in chisel3.experimental._", "3.2") - val FixedPoint = chisel3.experimental.FixedPoint - @deprecated("Use the version in chisel3.experimental._", "3.2") - implicit class fromDoubleToLiteral(double: Double) extends experimental.FixedPoint.Implicits.fromDoubleToLiteral(double) - @deprecated("Use the version in chisel3.experimental._", "3.2") - implicit class fromIntToBinaryPoint(int: Int) extends chisel3.fromIntToBinaryPoint(int) - @deprecated("Use the version in chisel3.experimental._", "3.2") - type RunFirrtlTransform = chisel3.experimental.RunFirrtlTransform - - @deprecated("Use the version in chisel3.experimental._", "3.2") - val annotate = chisel3.experimental.annotate - - @deprecated("Use the version in chisel3.experimental._", "3.2") - val DataMirror = chisel3.experimental.DataMirror - @deprecated("Use the version in chisel3._", "3.2") - type ActualDirection = chisel3.ActualDirection - @deprecated("Use the version in chisel3._", "3.2") - val ActualDirection = chisel3.ActualDirection - - @deprecated("Use the version in chisel3.internal._", "3.2") - val requireIsHardware = chisel3.internal.requireIsHardware - @deprecated("Use the version in chisel3.internal._", "3.2") - val requireIsChiselType = chisel3.internal.requireIsChiselType - @deprecated("Use the version in chisel3.internal._", "3.2") - val BiConnect = chisel3.internal.BiConnect - @deprecated("Use the version in chisel3.internal._", "3.2") - val MonoConnect = chisel3.internal.MonoConnect - @deprecated("Use the version in chisel3.internal._", "3.2") - val BindingDirection = chisel3.internal.BindingDirection - @deprecated("Use the version in chisel3.internal._", "3.2") - type Binding = chisel3.internal.Binding - @deprecated("Use the version in chisel3.internal._", "3.2") - type TopBinding = chisel3.internal.TopBinding - @deprecated("Use the version in chisel3.internal._", "3.2") - type UnconstrainedBinding = chisel3.internal.UnconstrainedBinding - @deprecated("Use the version in chisel3.internal._", "3.2") - type ConstrainedBinding = chisel3.internal.ConstrainedBinding - @deprecated("Use the version in chisel3.internal._", "3.2") - type ReadOnlyBinding = chisel3.internal.ReadOnlyBinding - @deprecated("Use the version in chisel3.internal._", "3.2") - type OpBinding = chisel3.internal.OpBinding - @deprecated("Use the version in chisel3.internal._", "3.2") - type MemoryPortBinding = chisel3.internal.MemoryPortBinding - @deprecated("Use the version in chisel3.internal._", "3.2") - type PortBinding = chisel3.internal.PortBinding - @deprecated("Use the version in chisel3.internal._", "3.2") - type RegBinding = chisel3.internal.RegBinding - @deprecated("Use the version in chisel3.internal._", "3.2") - type WireBinding = chisel3.internal.WireBinding - @deprecated("Use the version in chisel3.internal._", "3.2") - type ChildBinding = chisel3.internal.ChildBinding - @deprecated("Use the version in chisel3.internal._", "3.2") - type DontCareBinding = chisel3.internal.DontCareBinding - @deprecated("Use the version in chisel3.internal._", "3.2") - type LitBinding = chisel3.internal.LitBinding - @deprecated("Use the version in chisel3.internal._", "3.2") - type ElementLitBinding = chisel3.internal.ElementLitBinding - @deprecated("Use the version in chisel3.internal._", "3.2") - type BundleLitBinding = chisel3.internal.BundleLitBinding -} diff --git a/chiselFrontend/src/main/scala/chisel3/dontTouch.scala b/chiselFrontend/src/main/scala/chisel3/dontTouch.scala deleted file mode 100644 index 5dfd9f19..00000000 --- a/chiselFrontend/src/main/scala/chisel3/dontTouch.scala +++ /dev/null @@ -1,37 +0,0 @@ -package chisel3 - -import chisel3.experimental.{ChiselAnnotation, annotate, requireIsHardware} -import firrtl.transforms.DontTouchAnnotation - -/** Marks that a signal should not be removed by Chisel and Firrtl optimization passes - * - * @example {{{ - * class MyModule extends Module { - * val io = IO(new Bundle { - * val a = Input(UInt(32.W)) - * val b = Output(UInt(32.W)) - * }) - * io.b := io.a - * val dead = io.a +% 1.U // normally dead would be pruned by DCE - * dontTouch(dead) // Marking it as such will preserve it - * } - * }}} - * @note Calling this on [[Data]] creates an annotation that Chisel emits to a separate annotations - * file. This file must be passed to FIRRTL independently of the `.fir` file. The execute methods - * in [[chisel3.Driver]] will pass the annotations to FIRRTL automatically. - */ -object dontTouch { // scalastyle:ignore object.name - /** Marks a signal to be preserved in Chisel and Firrtl - * - * @note Requires the argument to be bound to hardware - * @param data The signal to be marked - * @return Unmodified signal `data` - */ - def apply[T <: Data](data: T)(implicit compileOptions: CompileOptions): T = { - if (compileOptions.checkSynthesizable) { - requireIsHardware(data, "Data marked dontTouch") - } - annotate(new ChiselAnnotation { def toFirrtl = DontTouchAnnotation(data.toNamed) }) - data - } -} diff --git a/chiselFrontend/src/main/scala/chisel3/experimental/Analog.scala b/chiselFrontend/src/main/scala/chisel3/experimental/Analog.scala deleted file mode 100644 index 37eb578d..00000000 --- a/chiselFrontend/src/main/scala/chisel3/experimental/Analog.scala +++ /dev/null @@ -1,85 +0,0 @@ -// See LICENSE for license details. - -package chisel3.experimental - -import chisel3.internal.firrtl.Width -import chisel3.internal.sourceinfo.SourceInfo -import chisel3.internal._ -import chisel3.{ActualDirection, Bits, CompileOptions, Data, Element, PString, Printable, RawModule, SpecifiedDirection, UInt} - -import scala.collection.mutable - -/** Data type for representing bidirectional bitvectors of a given width - * - * Analog support is limited to allowing wiring up of Verilog BlackBoxes with bidirectional (inout) - * pins. There is currently no support for reading or writing of Analog types within Chisel code. - * - * Given that Analog is bidirectional, it is illegal to assign a direction to any Analog type. It - * is legal to "flip" the direction (since Analog can be a member of aggregate types) which has no - * effect. - * - * Analog types are generally connected using the bidirectional [[attach]] mechanism, but also - * support limited bulkconnect `<>`. Analog types are only allowed to be bulk connected *once* in a - * given module. This is to prevent any surprising consequences of last connect semantics. - * - * @note This API is experimental and subject to change - */ -final class Analog private (private[chisel3] val width: Width) extends Element { - require(width.known, "Since Analog is only for use in BlackBoxes, width must be known") - - override def toString: String = { - s"Analog$width$bindingToString" - } - - private[chisel3] override def typeEquivalent(that: Data): Boolean = - that.isInstanceOf[Analog] && this.width == that.width - - override def litOption: Option[BigInt] = None - - def cloneType: this.type = new Analog(width).asInstanceOf[this.type] - - // Used to enforce single bulk connect of Analog types, multi-attach is still okay - // Note that this really means 1 bulk connect per Module because a port can - // be connected in the parent module as well - private[chisel3] val biConnectLocs = mutable.Map.empty[RawModule, SourceInfo] - - // Define setter/getter pairing - // Analog can only be bound to Ports and Wires (and Unbound) - private[chisel3] override def bind(target: Binding, parentDirection: SpecifiedDirection) { - SpecifiedDirection.fromParent(parentDirection, specifiedDirection) match { - case SpecifiedDirection.Unspecified | SpecifiedDirection.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 - // See https://github.com/freechipsproject/chisel3/pull/946 - case SampleElementBinding(parent) => parent.topBinding - } - - targetTopBinding match { - case _: WireBinding | _: 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 = - throwException("Analog does not support asUInt") - - private[chisel3] override def connectFromBits(that: Bits)(implicit sourceInfo: SourceInfo, - compileOptions: CompileOptions): Unit = { - throwException("Analog does not support connectFromBits") - } - - def toPrintable: Printable = PString("Analog") -} - -/** Object that provides factory methods for [[Analog]] objects - * - * @note This API is experimental and subject to change - */ -object Analog { - def apply(width: Width): Analog = new Analog(width) -} - diff --git a/chiselFrontend/src/main/scala/chisel3/experimental/package.scala b/chiselFrontend/src/main/scala/chisel3/experimental/package.scala deleted file mode 100644 index 985f7715..00000000 --- a/chiselFrontend/src/main/scala/chisel3/experimental/package.scala +++ /dev/null @@ -1,140 +0,0 @@ -// See LICENSE for license details. - -package chisel3 - -/** Package for experimental features, which may have their API changed, be removed, etc. - * - * Because its contents won't necessarily have the same level of stability and support as - * non-experimental, you must explicitly import this package to use its contents. - */ -package object experimental { // scalastyle:ignore object.name - import scala.language.implicitConversions - import chisel3.internal.BaseModule - - // Implicit conversions for BlackBox Parameters - implicit def fromIntToIntParam(x: Int): IntParam = IntParam(BigInt(x)) - implicit def fromLongToIntParam(x: Long): IntParam = IntParam(BigInt(x)) - implicit def fromBigIntToIntParam(x: BigInt): IntParam = IntParam(x) - implicit def fromDoubleToDoubleParam(x: Double): DoubleParam = DoubleParam(x) - implicit def fromStringToStringParam(x: String): StringParam = StringParam(x) - - type ChiselEnum = EnumFactory - - @deprecated("Use the version in chisel3._", "3.2") - val withClockAndReset = chisel3.withClockAndReset - @deprecated("Use the version in chisel3._", "3.2") - val withClock = chisel3.withClock - @deprecated("Use the version in chisel3._", "3.2") - val withReset = chisel3.withReset - - // Rocket Chip-style clonemodule - - /** A record containing the results of CloneModuleAsRecord - * The apply method is retrieves the element with the supplied name. - */ - type ClonePorts = BaseModule.ClonePorts - - object CloneModuleAsRecord { - /** Clones an existing module and returns a record of all its top-level ports. - * Each element of the record is named with a string matching the - * corresponding port's name and shares the port's type. - * @example {{{ - * val q1 = Module(new Queue(UInt(32.W), 2)) - * val q2_io = CloneModuleAsRecord(q1)("io").asInstanceOf[q1.io.type] - * q2_io.enq <> q1.io.deq - * }}} - */ - def apply(proto: BaseModule)(implicit sourceInfo: chisel3.internal.sourceinfo.SourceInfo, compileOptions: CompileOptions): ClonePorts = { // scalastyle:ignore line.size.limit - BaseModule.cloneIORecord(proto) - } - } - - val requireIsHardware = chisel3.internal.requireIsHardware - val requireIsChiselType = chisel3.internal.requireIsChiselType - type Direction = ActualDirection - val Direction = ActualDirection - - implicit class ChiselRange(val sc: StringContext) extends AnyVal { - - import scala.language.experimental.macros - - /** Specifies a range using mathematical range notation. Variables can be interpolated using - * standard string interpolation syntax. - * @example {{{ - * UInt(range"[0, 2)") - * UInt(range"[0, \$myInt)") - * UInt(range"[0, \${myInt + 2})") - * }}} - */ - def range(args: Any*): chisel3.internal.firrtl.IntervalRange = macro chisel3.internal.RangeTransform.apply - } - - class dump extends chisel3.internal.naming.dump // scalastyle:ignore class.name - class treedump extends chisel3.internal.naming.treedump // scalastyle:ignore class.name - /** Experimental macro for naming Chisel hardware values - * - * By default, Chisel uses reflection for naming which only works for public fields of `Bundle` - * and `Module` classes. Applying this macro annotation to a `class` or `object` enables Chisel - * to name any hardware values within the annotated `class` or `object. - * - * @example {{{ - * import chisel3._ - * import chisel3.experimental.chiselName - * - * @chiselName - * class MyModule extends Module { - * val io = IO(new Bundle { - * val in = Input(UInt(8.W)) - * val out = Output(UInt(8.W)) - * }) - * def createReg(): Unit = { - * // @chiselName allows Chisel to name this Reg - * val myReg = RegInit(io.in) - * io.out := myReg - * } - * createReg() - * } - * }}} - */ - class chiselName extends chisel3.internal.naming.chiselName // scalastyle:ignore class.name - /** Do not name instances of this type in [[chiselName]] - * - * By default, `chiselName` will include `val` names of instances of annotated classes as a - * prefix in final naming. Mixing in this trait to a `class`, `object`, or anonymous `class` - * instances will exclude the `val` name from `chiselName` naming. - * - * @example {{{ - * import chisel3._ - * import chisel3.experimental.{chiselName, NoChiselNamePrefix} - * - * // Note that this is not a Module - * @chiselName - * class Counter(w: Int) { - * val myReg = RegInit(0.U(w.W)) - * myReg := myReg + 1.U - * } - * - * @chiselName - * class MyModule extends Module { - * val io = IO(new Bundle { - * val out = Output(UInt(8.W)) - * }) - * // Name of myReg will be "counter0_myReg" - * val counter0 = new Counter(8) - * // Name of myReg will be "myReg" - * val counter1 = new Counter(8) with NoChiselNamePrefix - * io.out := counter0.myReg + counter1.myReg - * } - * }}} - */ - trait NoChiselNamePrefix - - object BundleLiterals { - implicit class AddBundleLiteralConstructor[T <: Bundle](x: T) { - //scalastyle:off method.name - def Lit(elems: (T => (Data, Data))*): T = { - x._makeLit(elems: _*) - } - } - } -} diff --git a/chiselFrontend/src/main/scala/chisel3/internal/BiConnect.scala b/chiselFrontend/src/main/scala/chisel3/internal/BiConnect.scala deleted file mode 100644 index 6b4c1070..00000000 --- a/chiselFrontend/src/main/scala/chisel3/internal/BiConnect.scala +++ /dev/null @@ -1,333 +0,0 @@ -// See LICENSE for license details. - -package chisel3.internal - -import chisel3._ -import chisel3.experimental.{Analog, BaseModule, attach} -import chisel3.internal.Builder.pushCommand -import chisel3.internal.firrtl.{Connect, DefInvalid} -import scala.language.experimental.macros -import chisel3.internal.sourceinfo._ - -/** -* BiConnect.connect executes a bidirectional connection element-wise. -* -* Note that the arguments are left and right (not source and sink) so the -* intent is for the operation to be commutative. -* -* The connect operation will recurse down the left Data (with the right Data). -* An exception will be thrown if a movement through the left cannot be matched -* in the right (or if the right side has extra fields). -* -* See elemConnect for details on how the root connections are issued. -* -*/ - -private[chisel3] object BiConnect { - // scalastyle:off method.name public.methods.have.type - // These are all the possible exceptions that can be thrown. - // These are from element-level connection - def BothDriversException = - BiConnectException(": Both Left and Right are drivers") - def NeitherDriverException = - BiConnectException(": Neither Left nor Right is a driver") - def UnknownDriverException = - BiConnectException(": Locally unclear whether Left or Right (both internal)") - def UnknownRelationException = - BiConnectException(": Left or Right unavailable to current module.") - // These are when recursing down aggregate types - def MismatchedVecException = - BiConnectException(": Left and Right are different length Vecs.") - def MissingLeftFieldException(field: String) = - BiConnectException(s".$field: Left Record missing field ($field).") - def MissingRightFieldException(field: String) = - BiConnectException(s": Right Record missing field ($field).") - def MismatchedException(left: String, right: String) = - BiConnectException(s": Left ($left) and Right ($right) have different types.") - def AttachAlreadyBulkConnectedException(sourceInfo: SourceInfo) = - BiConnectException(sourceInfo.makeMessage(": Analog previously bulk connected at " + _)) - def DontCareCantBeSink = - BiConnectException(": DontCare cannot be a connection sink (LHS)") - // scalastyle:on method.name public.methods.have.type - - /** This function is what recursively tries to connect a left and right together - * - * There is some cleverness in the use of internal try-catch to catch exceptions - * during the recursive decent and then rethrow them with extra information added. - * This gives the user a 'path' to where in the connections things went wrong. - */ - def connect(sourceInfo: SourceInfo, connectCompileOptions: CompileOptions, left: Data, right: Data, context_mod: RawModule): Unit = { // scalastyle:ignore line.size.limit cyclomatic.complexity method.length - (left, right) match { - // Handle element case (root case) - case (left_a: Analog, right_a: Analog) => - try { - markAnalogConnected(sourceInfo, left_a, context_mod) - markAnalogConnected(sourceInfo, right_a, context_mod) - } catch { // convert attach exceptions to BiConnectExceptions - case attach.AttachException(message) => throw BiConnectException(message) - } - attach.impl(Seq(left_a, right_a), context_mod)(sourceInfo) - case (left_a: Analog, DontCare) => - try { - markAnalogConnected(sourceInfo, left_a, context_mod) - } catch { // convert attach exceptions to BiConnectExceptions - case attach.AttachException(message) => throw BiConnectException(message) - } - pushCommand(DefInvalid(sourceInfo, left_a.lref)) - case (DontCare, right_a: Analog) => connect(sourceInfo, connectCompileOptions, right, left, context_mod) - case (left_e: Element, right_e: Element) => { - elemConnect(sourceInfo, connectCompileOptions, left_e, right_e, context_mod) - // TODO(twigg): Verify the element-level classes are connectable - } - // Handle Vec case - case (left_v: Vec[Data@unchecked], right_v: Vec[Data@unchecked]) => { - if (left_v.length != right_v.length) { - throw MismatchedVecException - } - for (idx <- 0 until left_v.length) { - try { - implicit val compileOptions = connectCompileOptions - connect(sourceInfo, connectCompileOptions, left_v(idx), right_v(idx), context_mod) - } catch { - case BiConnectException(message) => throw BiConnectException(s"($idx)$message") - } - } - } - // Handle Vec connected to DontCare - case (left_v: Vec[Data@unchecked], DontCare) => { - for (idx <- 0 until left_v.length) { - try { - implicit val compileOptions = connectCompileOptions - connect(sourceInfo, connectCompileOptions, left_v(idx), right, context_mod) - } catch { - case BiConnectException(message) => throw BiConnectException(s"($idx)$message") - } - } - } - // Handle DontCare connected to Vec - case (DontCare, right_v: Vec[Data@unchecked]) => { - for (idx <- 0 until right_v.length) { - try { - implicit val compileOptions = connectCompileOptions - connect(sourceInfo, connectCompileOptions, left, right_v(idx), context_mod) - } catch { - case BiConnectException(message) => throw BiConnectException(s"($idx)$message") - } - } - } - // Handle Records defined in Chisel._ code (change to NotStrict) - case (left_r: Record, right_r: Record) => (left_r.compileOptions, right_r.compileOptions) match { - case (ExplicitCompileOptions.NotStrict, _) => - left_r.bulkConnect(right_r)(sourceInfo, ExplicitCompileOptions.NotStrict) - case (_, ExplicitCompileOptions.NotStrict) => - left_r.bulkConnect(right_r)(sourceInfo, ExplicitCompileOptions.NotStrict) - case _ => recordConnect(sourceInfo, connectCompileOptions, left_r, right_r, context_mod) - } - - // Handle Records connected to DontCare (change to NotStrict) - case (left_r: Record, DontCare) => - left_r.compileOptions match { - case ExplicitCompileOptions.NotStrict => - left.bulkConnect(right)(sourceInfo, ExplicitCompileOptions.NotStrict) - case _ => - // For each field in left, descend with right - for ((field, left_sub) <- left_r.elements) { - try { - connect(sourceInfo, connectCompileOptions, left_sub, right, context_mod) - } catch { - case BiConnectException(message) => throw BiConnectException(s".$field$message") - } - } - } - case (DontCare, right_r: Record) => - right_r.compileOptions match { - case ExplicitCompileOptions.NotStrict => - left.bulkConnect(right)(sourceInfo, ExplicitCompileOptions.NotStrict) - case _ => - // For each field in left, descend with right - for ((field, right_sub) <- right_r.elements) { - try { - connect(sourceInfo, connectCompileOptions, left, right_sub, context_mod) - } catch { - case BiConnectException(message) => throw BiConnectException(s".$field$message") - } - } - } - - // Left and right are different subtypes of Data so fail - case (left, right) => throw MismatchedException(left.toString, right.toString) - } - } - - // Do connection of two Records - def recordConnect(sourceInfo: SourceInfo, - connectCompileOptions: CompileOptions, - left_r: Record, - right_r: Record, - context_mod: RawModule): Unit = { - // Verify right has no extra fields that left doesn't have - for((field, right_sub) <- right_r.elements) { - if(!left_r.elements.isDefinedAt(field)) { - if (connectCompileOptions.connectFieldsMustMatch) { - throw MissingLeftFieldException(field) - } - } - } - // For each field in left, descend with right - for((field, left_sub) <- left_r.elements) { - try { - right_r.elements.get(field) match { - case Some(right_sub) => connect(sourceInfo, connectCompileOptions, left_sub, right_sub, context_mod) - case None => { - if (connectCompileOptions.connectFieldsMustMatch) { - throw MissingRightFieldException(field) - } - } - } - } catch { - case BiConnectException(message) => throw BiConnectException(s".$field$message") - } - } - } - - - // These functions (finally) issue the connection operation - // Issue with right as sink, left as source - private def issueConnectL2R(left: Element, right: Element)(implicit sourceInfo: SourceInfo): Unit = { - // Source and sink are ambiguous in the case of a Bi/Bulk Connect (<>). - // If either is a DontCareBinding, just issue a DefInvalid for the other, - // otherwise, issue a Connect. - (left.topBinding, right.topBinding) match { - case (lb: DontCareBinding, _) => pushCommand(DefInvalid(sourceInfo, right.lref)) - case (_, rb: DontCareBinding) => pushCommand(DefInvalid(sourceInfo, left.lref)) - case (_, _) => pushCommand(Connect(sourceInfo, right.lref, left.ref)) - } - } - // Issue with left as sink, right as source - private def issueConnectR2L(left: Element, right: Element)(implicit sourceInfo: SourceInfo): Unit = { - // Source and sink are ambiguous in the case of a Bi/Bulk Connect (<>). - // If either is a DontCareBinding, just issue a DefInvalid for the other, - // otherwise, issue a Connect. - (left.topBinding, right.topBinding) match { - case (lb: DontCareBinding, _) => pushCommand(DefInvalid(sourceInfo, right.lref)) - case (_, rb: DontCareBinding) => pushCommand(DefInvalid(sourceInfo, left.lref)) - case (_, _) => pushCommand(Connect(sourceInfo, left.lref, right.ref)) - } - } - - // This function checks if element-level connection operation allowed. - // Then it either issues it or throws the appropriate exception. - def elemConnect(implicit sourceInfo: SourceInfo, connectCompileOptions: CompileOptions, left: Element, right: Element, context_mod: RawModule): Unit = { // scalastyle:ignore line.size.limit cyclomatic.complexity method.length - 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.topBinding.location.getOrElse(context_mod) - val right_mod: BaseModule = right.topBinding.location.getOrElse(context_mod) - - 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) && - (right_mod._parent.map(_ == context_mod).getOrElse(false)) ) { - // 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 (Input, Input) => issueConnectL2R(left, right) - case (Internal, Input) => issueConnectL2R(left, right) - - case (Output, Output) => issueConnectR2L(left, right) - case (Internal, Output) => issueConnectR2L(left, right) - - case (Input, Output) => throw BothDriversException - case (Output, Input) => throw NeitherDriverException - case (_, Internal) => throw UnknownRelationException - } - } - - // CASE: Context is same module as right node and left node is in child module - else if( (right_mod == context_mod) && - (left_mod._parent.map(_ == context_mod).getOrElse(false)) ) { - // 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 (Input, Input) => issueConnectR2L(left, right) - case (Input, Internal) => issueConnectR2L(left, right) - - case (Output, Output) => issueConnectL2R(left, right) - case (Output, Internal) => issueConnectL2R(left, right) - - case (Input, Output) => throw NeitherDriverException - case (Output, Input) => throw BothDriversException - case (Internal, _) => throw UnknownRelationException - } - } - - // CASE: Context is same module that both left node and right node are in - else if( (context_mod == left_mod) && (context_mod == right_mod) ) { - ((left_direction, right_direction): @unchecked) match { - // CURRENT MOD CURRENT MOD - case (Input, Output) => issueConnectL2R(left, right) - case (Input, Internal) => issueConnectL2R(left, right) - case (Internal, Output) => issueConnectL2R(left, right) - - case (Output, Input) => issueConnectR2L(left, right) - case (Output, Internal) => issueConnectR2L(left, right) - case (Internal, Input) => issueConnectR2L(left, right) - - case (Input, Input) => throw BothDriversException - case (Output, Output) => throw BothDriversException - case (Internal, Internal) => { - if (connectCompileOptions.dontAssumeDirectionality) { - throw UnknownDriverException - } else { - issueConnectR2L(left, right) - } - } - } - } - - // CASE: Context is the parent module of both the module containing left node - // and the module containing right node - // Note: This includes case when left and right in same module but in parent - else if( (left_mod._parent.map(_ == context_mod).getOrElse(false)) && - (right_mod._parent.map(_ == context_mod).getOrElse(false)) - ) { - // Thus both nodes must be ports and have a direction hint - ((left_direction, right_direction): @unchecked) match { - // CHILD MOD CHILD MOD - case (Input, Output) => issueConnectR2L(left, right) - case (Output, Input) => issueConnectL2R(left, right) - - case (Input, Input) => throw NeitherDriverException - case (Output, Output) => throw BothDriversException - case (_, Internal) => - if (connectCompileOptions.dontAssumeDirectionality) { - throw UnknownRelationException - } else { - issueConnectR2L(left, right) - } - case (Internal, _) => - if (connectCompileOptions.dontAssumeDirectionality) { - throw UnknownRelationException - } else { - issueConnectR2L(left, right) - } - } - } - - // Not quite sure where left and right are compared to current module - // so just error out - else throw UnknownRelationException - } - - // This function checks if analog element-level attaching is allowed, then marks the Analog as connected - def markAnalogConnected(implicit sourceInfo: SourceInfo, analog: Analog, contextModule: RawModule): Unit = { - analog.biConnectLocs.get(contextModule) match { - case Some(sl) => throw AttachAlreadyBulkConnectedException(sl) - case None => // Do nothing - } - // Mark bulk connected - analog.biConnectLocs(contextModule) = sourceInfo - } -} diff --git a/chiselFrontend/src/main/scala/chisel3/internal/Binding.scala b/chiselFrontend/src/main/scala/chisel3/internal/Binding.scala deleted file mode 100644 index 07c44f9f..00000000 --- a/chiselFrontend/src/main/scala/chisel3/internal/Binding.scala +++ /dev/null @@ -1,114 +0,0 @@ -// See LICENSE for license details. - -package chisel3.internal - -import chisel3._ -import chisel3.experimental.BaseModule -import chisel3.internal.firrtl.LitArg - -/** Requires that a node is hardware ("bound") - */ -object requireIsHardware { - def apply(node: Data, msg: String = ""): Unit = { - node._parent match { // Compatibility layer hack - case Some(x: BaseModule) => x._compatAutoWrapPorts - case _ => - } - if (!node.isSynthesizable) { - val prefix = if (msg.nonEmpty) s"$msg " else "" - throw ExpectedHardwareException(s"$prefix'$node' must be hardware, " + - "not a bare Chisel type. Perhaps you forgot to wrap it in Wire(_) or IO(_)?") - } - } -} - -/** Requires that a node is a chisel type (not hardware, "unbound") - */ -object requireIsChiselType { - def apply(node: Data, msg: String = ""): Unit = if (node.isSynthesizable) { - val prefix = if (msg.nonEmpty) s"$msg " else "" - throw ExpectedChiselTypeException(s"$prefix'$node' must be a Chisel type, not hardware") - } -} - -// Element only direction used for the Binding system only. -private[chisel3] sealed abstract class BindingDirection -private[chisel3] 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): BindingDirection = { - 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'") - } - case _ => Internal - } - } -} - -// Location refers to 'where' in the Module hierarchy this lives -sealed trait Binding { - def location: Option[BaseModule] -} -// 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 TopBinding { - def location: Option[BaseModule] = None -} -// A constrained binding can only be read/written by specific modules -// Location will track where this Module is, and the bound object can be referenced in FIRRTL -sealed trait ConstrainedBinding extends TopBinding { - def enclosure: BaseModule - def location: Option[BaseModule] = { - // If an aspect is present, return the aspect module. Otherwise, return the enclosure module - // This allows aspect modules to pretend to be enclosed modules for connectivity checking, - // inside vs outside instance checking, etc. - Builder.aspectModule(enclosure) match { - case None => Some(enclosure) - case Some(aspect) => Some(aspect) - } - } -} - -// A binding representing a data that cannot be (re)assigned to. -sealed trait ReadOnlyBinding extends TopBinding - -// TODO(twigg): Ops between unenclosed nodes can also be unenclosed -// However, Chisel currently binds all op results to a module -case class OpBinding(enclosure: RawModule) extends ConstrainedBinding with ReadOnlyBinding -case class MemoryPortBinding(enclosure: RawModule) extends ConstrainedBinding -case class PortBinding(enclosure: BaseModule) extends ConstrainedBinding -case class RegBinding(enclosure: RawModule) extends ConstrainedBinding -case class WireBinding(enclosure: RawModule) extends ConstrainedBinding - -case class ChildBinding(parent: Data) extends Binding { - def location: Option[BaseModule] = parent.topBinding.location -} -/** Special binding for Vec.sample_element */ -case class SampleElementBinding[T <: Data](parent: Vec[T]) extends Binding { - def location = parent.topBinding.location -} -// A DontCare element has a specific Binding, somewhat like a literal. -// It is a source (RHS). It may only be connected/applied to sinks. -case class DontCareBinding() extends UnconstrainedBinding - -sealed trait LitBinding extends UnconstrainedBinding with ReadOnlyBinding -// Literal binding attached to a element that is not part of a Bundle. -case class ElementLitBinding(litArg: LitArg) extends LitBinding -// Literal binding attached to the root of a Bundle, containing literal values of its children. -case class BundleLitBinding(litMap: Map[Data, LitArg]) extends LitBinding diff --git a/chiselFrontend/src/main/scala/chisel3/internal/Builder.scala b/chiselFrontend/src/main/scala/chisel3/internal/Builder.scala deleted file mode 100644 index 773a9ad1..00000000 --- a/chiselFrontend/src/main/scala/chisel3/internal/Builder.scala +++ /dev/null @@ -1,452 +0,0 @@ -// See LICENSE for license details. - -package chisel3.internal - -import scala.util.DynamicVariable -import scala.collection.mutable.ArrayBuffer -import chisel3._ -import chisel3.experimental._ -import chisel3.internal.firrtl._ -import chisel3.internal.naming._ -import _root_.firrtl.annotations.{CircuitName, ComponentName, IsMember, ModuleName, Named, ReferenceTarget} - -import scala.collection.mutable - -private[chisel3] class Namespace(keywords: Set[String]) { - private val names = collection.mutable.HashMap[String, Long]() - for (keyword <- keywords) - names(keyword) = 1 - - private def rename(n: String): String = { - val index = names(n) - val tryName = s"${n}_${index}" - names(n) = index + 1 - if (this contains tryName) rename(n) else tryName - } - - private def sanitize(s: String, leadingDigitOk: Boolean = false): String = { - // TODO what character set does FIRRTL truly support? using ANSI C for now - def legalStart(c: Char) = (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' - def legal(c: Char) = legalStart(c) || (c >= '0' && c <= '9') - val res = s filter legal - val headOk = (!res.isEmpty) && (leadingDigitOk || legalStart(res.head)) - if (headOk) res else s"_$res" - } - - def contains(elem: String): Boolean = names.contains(elem) - - // leadingDigitOk is for use in fields of Records - def name(elem: String, leadingDigitOk: Boolean = false): String = { - val sanitized = sanitize(elem, leadingDigitOk) - if (this contains sanitized) { - name(rename(sanitized)) - } else { - names(sanitized) = 1 - sanitized - } - } -} - -private[chisel3] object Namespace { - /** Constructs an empty Namespace */ - def empty: Namespace = new Namespace(Set.empty[String]) -} - -private[chisel3] class IdGen { - private var counter = -1L - def next: Long = { - counter += 1 - counter - } -} - -/** Public API to access Node/Signal names. - * currently, the node's name, the full path name, and references to its parent Module and component. - * These are only valid once the design has been elaborated, and should not be used during its construction. - */ -trait InstanceId { - def instanceName: String - def pathName: String - def parentPathName: String - def parentModName: String - /** Returns a FIRRTL Named that refers to this object in the elaborated hardware graph */ - def toNamed: Named - /** Returns a FIRRTL IsMember that refers to this object in the elaborated hardware graph */ - def toTarget: IsMember - /** Returns a FIRRTL IsMember that refers to the absolute path to this object in the elaborated hardware graph */ - def toAbsoluteTarget: IsMember -} - -private[chisel3] trait HasId extends InstanceId { - private[chisel3] def _onModuleClose: Unit = {} // scalastyle:ignore method.name - private[chisel3] val _parent: Option[BaseModule] = Builder.currentModule - _parent.foreach(_.addId(this)) - - private[chisel3] val _id: Long = Builder.idGen.next - - // TODO: remove this, but its removal seems to cause a nasty Scala compiler crash. - override def hashCode: Int = super.hashCode() - override def equals(that: Any): Boolean = super.equals(that) - - // Facilities for 'suggesting' a name to this. - // Post-name hooks called to carry the suggestion to other candidates as needed - private var suggested_name: Option[String] = None - private val postname_hooks = scala.collection.mutable.ListBuffer.empty[String=>Unit] - // Only takes the first suggestion! - def suggestName(name: =>String): this.type = { - if(suggested_name.isEmpty) suggested_name = Some(name) - for(hook <- postname_hooks) { hook(name) } - this - } - private[chisel3] def suggestedName: Option[String] = suggested_name - private[chisel3] def addPostnameHook(hook: String=>Unit): Unit = postname_hooks += hook - - // Uses a namespace to convert suggestion into a true name - // Will not do any naming if the reference already assigned. - // (e.g. tried to suggest a name to part of a Record) - private[chisel3] def forceName(default: =>String, namespace: Namespace): Unit = - if(_ref.isEmpty) { - val candidate_name = suggested_name.getOrElse(default) - val available_name = namespace.name(candidate_name) - setRef(Ref(available_name)) - } - - private var _ref: Option[Arg] = None - private[chisel3] def setRef(imm: Arg): Unit = _ref = Some(imm) - private[chisel3] def setRef(parent: HasId, name: String): Unit = setRef(Slot(Node(parent), name)) - private[chisel3] def setRef(parent: HasId, index: Int): Unit = setRef(Index(Node(parent), ILit(index))) - private[chisel3] def setRef(parent: HasId, index: UInt): Unit = setRef(Index(Node(parent), index.ref)) - private[chisel3] def getRef: Arg = _ref.get - private[chisel3] def getOptionRef: Option[Arg] = _ref - - // Implementation of public methods. - def instanceName: String = _parent match { - case Some(p) => p._component match { - case Some(c) => _ref match { - case Some(arg) => arg fullName c - case None => suggested_name.getOrElse("??") - } - case None => throwException("signalName/pathName should be called after circuit elaboration") - } - case None => throwException("this cannot happen") - } - def pathName: String = _parent match { - case None => instanceName - case Some(p) => s"${p.pathName}.$instanceName" - } - def parentPathName: String = _parent match { - case Some(p) => p.pathName - case None => throwException(s"$instanceName doesn't have a parent") - } - def parentModName: String = _parent match { - case Some(p) => p.name - case None => throwException(s"$instanceName doesn't have a parent") - } - // TODO Should this be public? - protected def circuitName: String = _parent match { - case None => instanceName - case Some(p) => p.circuitName - } - - private[chisel3] def getPublicFields(rootClass: Class[_]): Seq[java.lang.reflect.Method] = { - // Suggest names to nodes using runtime reflection - def getValNames(c: Class[_]): Set[String] = { - if (c == rootClass) { - Set() - } else { - getValNames(c.getSuperclass) ++ c.getDeclaredFields.map(_.getName) - } - } - val valNames = getValNames(this.getClass) - def isPublicVal(m: java.lang.reflect.Method) = - m.getParameterTypes.isEmpty && valNames.contains(m.getName) && !m.getDeclaringClass.isAssignableFrom(rootClass) - this.getClass.getMethods.sortWith(_.getName < _.getName).filter(isPublicVal(_)) - } -} -/** Holds the implementation of toNamed for Data and MemBase */ -private[chisel3] trait NamedComponent extends HasId { - /** Returns a FIRRTL ComponentName that references this object - * @note Should not be called until circuit elaboration is complete - */ - final def toNamed: ComponentName = - ComponentName(this.instanceName, ModuleName(this.parentModName, CircuitName(this.circuitName))) - - /** Returns a FIRRTL ReferenceTarget that references this object - * @note Should not be called until circuit elaboration is complete - */ - final def toTarget: ReferenceTarget = { - val name = this.instanceName - import _root_.firrtl.annotations.{Target, TargetToken} - Target.toTargetTokens(name).toList match { - case TargetToken.Ref(r) :: components => ReferenceTarget(this.circuitName, this.parentModName, Nil, r, components) - case other => - throw _root_.firrtl.annotations.Target.NamedException(s"Cannot convert $name into [[ReferenceTarget]]: $other") - } - } - - final def toAbsoluteTarget: ReferenceTarget = { - val localTarget = toTarget - _parent match { - case Some(parent) => parent.toAbsoluteTarget.ref(localTarget.ref).copy(component = localTarget.component) - case None => localTarget - } - } -} - -// Mutable global state for chisel that can appear outside a Builder context -private[chisel3] class ChiselContext() { - val idGen = new IdGen - - // Record the Bundle instance, class name, method name, and reverse stack trace position of open Bundles - val bundleStack: ArrayBuffer[(Bundle, String, String, Int)] = ArrayBuffer() -} - -private[chisel3] class DynamicContext() { - val globalNamespace = Namespace.empty - val components = ArrayBuffer[Component]() - val annotations = ArrayBuffer[ChiselAnnotation]() - var currentModule: Option[BaseModule] = None - - /** Contains a mapping from a elaborated module to their aspect - * Set by [[ModuleAspect]] - */ - val aspectModule: mutable.HashMap[BaseModule, BaseModule] = mutable.HashMap.empty[BaseModule, BaseModule] - - // Set by object Module.apply before calling class Module constructor - // Used to distinguish between no Module() wrapping, multiple wrappings, and rewrapping - var readyForModuleConstr: Boolean = false - var whenDepth: Int = 0 // Depth of when nesting - var currentClock: Option[Clock] = None - var currentReset: Option[Reset] = None - val errors = new ErrorLog - val namingStack = new NamingStack -} - -//scalastyle:off number.of.methods -private[chisel3] object Builder { - // All global mutable state must be referenced via dynamicContextVar!! - private val dynamicContextVar = new DynamicVariable[Option[DynamicContext]](None) - private def dynamicContext: DynamicContext = { - require(dynamicContextVar.value.isDefined, "must be inside Builder context") - dynamicContextVar.value.get - } - - private val chiselContext = new DynamicVariable[ChiselContext](new ChiselContext) - - // Initialize any singleton objects before user code inadvertently inherits them. - private def initializeSingletons(): Unit = { - // This used to contain: - // val dummy = core.DontCare - // but this would occasionally produce hangs due to static initialization deadlock - // when Builder initialization collided with chisel3.package initialization of the DontCare value. - // See: - // http://ternarysearch.blogspot.com/2013/07/static-initialization-deadlock.html - // https://bugs.openjdk.java.net/browse/JDK-8037567 - // https://stackoverflow.com/questions/28631656/runnable-thread-state-but-in-object-wait - } - - def namingStackOption: Option[NamingStack] = dynamicContextVar.value.map(_.namingStack) - - def idGen: IdGen = chiselContext.value.idGen - - def globalNamespace: Namespace = dynamicContext.globalNamespace - def components: ArrayBuffer[Component] = dynamicContext.components - def annotations: ArrayBuffer[ChiselAnnotation] = dynamicContext.annotations - def namingStack: NamingStack = dynamicContext.namingStack - - def currentModule: Option[BaseModule] = dynamicContextVar.value match { - case Some(dyanmicContext) => dynamicContext.currentModule - case _ => None - } - def currentModule_=(target: Option[BaseModule]): Unit = { - dynamicContext.currentModule = target - } - def aspectModule(module: BaseModule): Option[BaseModule] = dynamicContextVar.value match { - case Some(dynamicContext) => dynamicContext.aspectModule.get(module) - case _ => None - } - def addAspect(module: BaseModule, aspect: BaseModule): Unit = { - dynamicContext.aspectModule += ((module, aspect)) - } - def forcedModule: BaseModule = currentModule match { - case Some(module) => module - case None => throwException( - "Error: Not in a Module. Likely cause: Missed Module() wrap or bare chisel API call." - // A bare api call is, e.g. calling Wire() from the scala console). - ) - } - def referenceUserModule: RawModule = { - currentModule match { - case Some(module: RawModule) => - aspectModule(module) match { - case Some(aspect: RawModule) => aspect - case other => module - } - case _ => throwException( - "Error: Not in a RawModule. Likely cause: Missed Module() wrap, bare chisel API call, or attempting to construct hardware inside a BlackBox." // scalastyle:ignore line.size.limit - // A bare api call is, e.g. calling Wire() from the scala console). - ) - } - } - def forcedUserModule: RawModule = currentModule match { - case Some(module: RawModule) => module - case _ => throwException( - "Error: Not in a UserModule. Likely cause: Missed Module() wrap, bare chisel API call, or attempting to construct hardware inside a BlackBox." // scalastyle:ignore line.size.limit - // A bare api call is, e.g. calling Wire() from the scala console). - ) - } - def readyForModuleConstr: Boolean = dynamicContext.readyForModuleConstr - def readyForModuleConstr_=(target: Boolean): Unit = { - dynamicContext.readyForModuleConstr = target - } - def whenDepth: Int = dynamicContext.whenDepth - def whenDepth_=(target: Int): Unit = { - dynamicContext.whenDepth = target - } - def currentClock: Option[Clock] = dynamicContext.currentClock - def currentClock_=(newClock: Option[Clock]): Unit = { - dynamicContext.currentClock = newClock - } - - def currentReset: Option[Reset] = dynamicContext.currentReset - def currentReset_=(newReset: Option[Reset]): Unit = { - dynamicContext.currentReset = newReset - } - - def forcedClock: Clock = currentClock.getOrElse( - throwException("Error: No implicit clock.") - ) - def forcedReset: Reset = currentReset.getOrElse( - throwException("Error: No implicit reset.") - ) - - // TODO(twigg): Ideally, binding checks and new bindings would all occur here - // However, rest of frontend can't support this yet. - def pushCommand[T <: Command](c: T): T = { - forcedUserModule.addCommand(c) - c - } - def pushOp[T <: Data](cmd: DefPrim[T]): T = { - // Bind each element of the returned Data to being a Op - cmd.id.bind(OpBinding(forcedUserModule)) - pushCommand(cmd).id - } - - // Called when Bundle construction begins, used to record a stack of open Bundle constructors to - // record candidates for Bundle autoclonetype. This is a best-effort guess. - // Returns the current stack of open Bundles - // Note: elt will NOT have finished construction, its elements cannot be accessed - def updateBundleStack(elt: Bundle): Seq[Bundle] = { - val stackElts = Thread.currentThread().getStackTrace() - .reverse // so stack frame numbers are deterministic across calls - .dropRight(2) // discard Thread.getStackTrace and updateBundleStack - - // Determine where we are in the Bundle stack - val eltClassName = elt.getClass.getName - val eltStackPos = stackElts.map(_.getClassName).lastIndexOf(eltClassName) - - // Prune the existing Bundle stack of closed Bundles - // If we know where we are in the stack, discard frames above that - val stackEltsTop = if (eltStackPos >= 0) eltStackPos else stackElts.size - val pruneLength = chiselContext.value.bundleStack.reverse.prefixLength { case (_, cname, mname, pos) => - pos >= stackEltsTop || stackElts(pos).getClassName != cname || stackElts(pos).getMethodName != mname - } - chiselContext.value.bundleStack.trimEnd(pruneLength) - - // Return the stack state before adding the most recent bundle - val lastStack = chiselContext.value.bundleStack.map(_._1).toSeq - - // Append the current Bundle to the stack, if it's on the stack trace - if (eltStackPos >= 0) { - val stackElt = stackElts(eltStackPos) - chiselContext.value.bundleStack.append((elt, eltClassName, stackElt.getMethodName, eltStackPos)) - } - // Otherwise discard the stack frame, this shouldn't fail noisily - - lastStack - } - - /** Recursively suggests names to supported "container" classes - * Arbitrary nestings of supported classes are allowed so long as the - * innermost element is of type HasId - * (Note: Map is Iterable[Tuple2[_,_]] and thus excluded) - */ - def nameRecursively(prefix: String, nameMe: Any, namer: (HasId, String) => Unit): Unit = nameMe match { - case (id: HasId) => namer(id, prefix) - case Some(elt) => nameRecursively(prefix, elt, namer) - case (iter: Iterable[_]) if iter.hasDefiniteSize => - for ((elt, i) <- iter.zipWithIndex) { - nameRecursively(s"${prefix}_${i}", elt, namer) - } - case _ => // Do nothing - } - - def errors: ErrorLog = dynamicContext.errors - def error(m: => String): Unit = if (dynamicContextVar.value.isDefined) errors.error(m) - def warning(m: => String): Unit = if (dynamicContextVar.value.isDefined) errors.warning(m) - def deprecated(m: => String, location: Option[String] = None): Unit = - if (dynamicContextVar.value.isDefined) errors.deprecated(m, location) - - /** Record an exception as an error, and throw it. - * - * @param m exception message - */ - @throws(classOf[ChiselException]) - def exception(m: => String): Unit = { - error(m) - throwException(m) - } - - def build[T <: RawModule](f: => T): (Circuit, T) = { - chiselContext.withValue(new ChiselContext) { - dynamicContextVar.withValue(Some(new DynamicContext())) { - errors.info("Elaborating design...") - val mod = f - mod.forceName(mod.name, globalNamespace) - errors.checkpoint() - errors.info("Done elaborating.") - - (Circuit(components.last.name, components, annotations), mod) - } - } - } - initializeSingletons() -} - -/** Allows public access to the naming stack in Builder / DynamicContext, and handles invocations - * outside a Builder context. - * Necessary because naming macros expand in user code and don't have access into private[chisel3] - * objects. - */ -object DynamicNamingStack { - def pushContext(): NamingContextInterface = { - Builder.namingStackOption match { - case Some(namingStack) => namingStack.pushContext() - case None => DummyNamer - } - } - - def popReturnContext[T <: Any](prefixRef: T, until: NamingContextInterface): T = { - until match { - case DummyNamer => - require(Builder.namingStackOption.isEmpty, - "Builder context must remain stable throughout a chiselName-annotated function invocation") - case context: NamingContext => - require(Builder.namingStackOption.isDefined, - "Builder context must remain stable throughout a chiselName-annotated function invocation") - Builder.namingStackOption.get.popContext(prefixRef, context) - } - prefixRef - } - - def length() : Int = Builder.namingStackOption.get.length -} - -/** Casts BigInt to Int, issuing an error when the input isn't representable. */ -private[chisel3] object castToInt { - def apply(x: BigInt, msg: String): Int = { - val res = x.toInt - require(x == res, s"$msg $x is too large to be represented as Int") - res - } -} diff --git a/chiselFrontend/src/main/scala/chisel3/internal/Error.scala b/chiselFrontend/src/main/scala/chisel3/internal/Error.scala deleted file mode 100644 index 369da52e..00000000 --- a/chiselFrontend/src/main/scala/chisel3/internal/Error.scala +++ /dev/null @@ -1,213 +0,0 @@ -// See LICENSE for license details. - -package chisel3.internal - -import scala.annotation.tailrec -import scala.collection.mutable.{ArrayBuffer, LinkedHashMap} - -class ChiselException(message: String, cause: Throwable = null) extends Exception(message, cause) { - - /** Package names whose stack trace elements should be trimmed when generating a trimmed stack trace */ - val blacklistPackages: Set[String] = Set("chisel3", "scala", "java", "sun", "sbt") - - /** The object name of Chisel's internal `Builder`. Everything stack trace element after this will be trimmed. */ - val builderName: String = chisel3.internal.Builder.getClass.getName - - /** Examine a [[Throwable]], to extract all its causes. Innermost cause is first. - * @param throwable an exception to examine - * @return a sequence of all the causes with innermost cause first - */ - @tailrec - private def getCauses(throwable: Throwable, acc: Seq[Throwable] = Seq.empty): Seq[Throwable] = - throwable.getCause() match { - case null => throwable +: acc - case a => getCauses(a, throwable +: acc) - } - - /** Returns true if an exception contains */ - private def containsBuilder(throwable: Throwable): Boolean = - throwable.getStackTrace().collectFirst { - case ste if ste.getClassName().startsWith(builderName) => throwable - }.isDefined - - /** Examine this [[ChiselException]] and it's causes for the first [[Throwable]] that contains a stack trace including - * a stack trace element whose declaring class is the [[builderName]]. If no such element exists, return this - * [[ChiselException]]. - */ - private lazy val likelyCause: Throwable = - getCauses(this).collectFirst{ case a if containsBuilder(a) => a }.getOrElse(this) - - /** For an exception, return a stack trace trimmed to user code only - * - * This does the following actions: - * - * 1. Trims the top of the stack trace while elements match [[blacklistPackages]] - * 2. Trims the bottom of the stack trace until an element matches [[builderName]] - * 3. Trims from the [[builderName]] all [[blacklistPackages]] - * - * @param throwable the exception whose stack trace should be trimmed - * @return an array of stack trace elements - */ - private def trimmedStackTrace(throwable: Throwable): Array[StackTraceElement] = { - def isBlacklisted(ste: StackTraceElement) = { - val packageName = ste.getClassName().takeWhile(_ != '.') - blacklistPackages.contains(packageName) - } - - val trimmedLeft = throwable.getStackTrace().view.dropWhile(isBlacklisted) - val trimmedReverse = trimmedLeft.reverse - .dropWhile(ste => !ste.getClassName.startsWith(builderName)) - .dropWhile(isBlacklisted) - trimmedReverse.reverse.toArray - } - - /** trims the top of the stack of elements belonging to [[blacklistPackages]] - * then trims the bottom elements until it reaches [[builderName]] - * then continues trimming elements belonging to [[blacklistPackages]] - */ - @deprecated("This method will be removed in 3.4", "3.3") - def trimmedStackTrace: Array[StackTraceElement] = trimmedStackTrace(this) - - def chiselStackTrace: String = { - val trimmed = trimmedStackTrace(likelyCause) - - val sw = new java.io.StringWriter - sw.write(likelyCause.toString + "\n") - sw.write("\t...\n") - trimmed.foreach(ste => sw.write(s"\tat $ste\n")) - sw.write("\t... (Stack trace trimmed to user code only, rerun with --full-stacktrace if you wish to see the full stack trace)\n") // scalastyle:ignore line.size.limit - sw.toString - } -} - -private[chisel3] object throwException { - def apply(s: String, t: Throwable = null): Nothing = - throw new ChiselException(s, t) -} - -/** Records and reports runtime errors and warnings. */ -private[chisel3] object ErrorLog { - val depTag = s"[${Console.BLUE}deprecated${Console.RESET}]" - val warnTag = s"[${Console.YELLOW}warn${Console.RESET}]" - val errTag = s"[${Console.RED}error${Console.RESET}]" -} - -private[chisel3] class ErrorLog { - /** Log an error message */ - def error(m: => String): Unit = - errors += new Error(m, getUserLineNumber) - - /** Log a warning message */ - def warning(m: => String): Unit = - errors += new Warning(m, getUserLineNumber) - - /** Emit an informational message */ - def info(m: String): Unit = - println(new Info("[%2.3f] %s".format(elapsedTime/1e3, m), None)) // scalastyle:ignore regex - - /** Log a deprecation warning message */ - def deprecated(m: => String, location: Option[String]): Unit = { - val sourceLoc = location match { - case Some(loc) => loc - case None => getUserLineNumber match { - case Some(elt: StackTraceElement) => s"${elt.getFileName}:${elt.getLineNumber}" - case None => "(unknown)" - } - } - - val thisEntry = (m, sourceLoc) - deprecations += ((thisEntry, deprecations.getOrElse(thisEntry, 0) + 1)) - } - - /** Throw an exception if any errors have yet occurred. */ - def checkpoint(): Unit = { - // scalastyle:off line.size.limit regex - deprecations.foreach { case ((message, sourceLoc), count) => - println(s"${ErrorLog.depTag} $sourceLoc ($count calls): $message") - } - errors foreach println - - if (!deprecations.isEmpty) { - println(s"${ErrorLog.warnTag} ${Console.YELLOW}There were ${deprecations.size} deprecated function(s) used." + - s" These may stop compiling in a future release - you are encouraged to fix these issues.${Console.RESET}") - println(s"${ErrorLog.warnTag} Line numbers for deprecations reported by Chisel may be inaccurate; enable scalac compiler deprecation warnings via either of the following methods:") - println(s"${ErrorLog.warnTag} In the sbt interactive console, enter:") - println(s"""${ErrorLog.warnTag} set scalacOptions in ThisBuild ++= Seq("-unchecked", "-deprecation")""") - println(s"${ErrorLog.warnTag} or, in your build.sbt, add the line:") - println(s"""${ErrorLog.warnTag} scalacOptions := Seq("-unchecked", "-deprecation")""") - } - - val allErrors = errors.filter(_.isFatal) - val allWarnings = errors.filter(!_.isFatal) - - if (!allWarnings.isEmpty && !allErrors.isEmpty) { - println(s"${ErrorLog.errTag} There were ${Console.RED}${allErrors.size} error(s)${Console.RESET} and ${Console.YELLOW}${allWarnings.size} warning(s)${Console.RESET} during hardware elaboration.") - } else if (!allWarnings.isEmpty) { - println(s"${ErrorLog.warnTag} There were ${Console.YELLOW}${allWarnings.size} warning(s)${Console.RESET} during hardware elaboration.") - } else if (!allErrors.isEmpty) { - println(s"${ErrorLog.errTag} There were ${Console.RED}${allErrors.size} error(s)${Console.RESET} during hardware elaboration.") - } - - if (!allErrors.isEmpty) { - throwException("Fatal errors during hardware elaboration") - } else { - // No fatal errors, clear accumulated warnings since they've been reported - errors.clear() - } - // scalastyle:on line.size.limit regex - } - - /** Returns the best guess at the first stack frame that belongs to user code. - */ - private def getUserLineNumber = { - def isChiselClassname(className: String): Boolean = { - // List of classpath prefixes that are Chisel internals and should be ignored when looking for user code - // utils are not part of internals and errors there can be reported - val chiselPrefixes = Set( - "java.", - "scala.", - "chisel3.internal.", - "chisel3.experimental.", - "chisel3.package$" // for some compatibility / deprecated types - ) - !chiselPrefixes.filter(className.startsWith(_)).isEmpty - } - - Thread.currentThread().getStackTrace.toList.dropWhile( - // Get rid of everything in Chisel core - ste => isChiselClassname(ste.getClassName) - ).headOption - } - - private val errors = ArrayBuffer[LogEntry]() - private val deprecations = LinkedHashMap[(String, String), Int]() - - private val startTime = System.currentTimeMillis - private def elapsedTime: Long = System.currentTimeMillis - startTime -} - -private abstract class LogEntry(msg: => String, line: Option[StackTraceElement]) { - def isFatal: Boolean = false - def format: String - - override def toString: String = line match { - case Some(l) => s"${format} ${l.getFileName}:${l.getLineNumber}: ${msg} in class ${l.getClassName}" - case None => s"${format} ${msg}" - } - - protected def tag(name: String, color: String): String = - s"[${color}${name}${Console.RESET}]" -} - -private class Error(msg: => String, line: Option[StackTraceElement]) extends LogEntry(msg, line) { - override def isFatal: Boolean = true - def format: String = tag("error", Console.RED) -} - -private class Warning(msg: => String, line: Option[StackTraceElement]) extends LogEntry(msg, line) { - def format: String = tag("warn", Console.YELLOW) -} - -private class Info(msg: => String, line: Option[StackTraceElement]) extends LogEntry(msg, line) { - def format: String = tag("info", Console.MAGENTA) -} diff --git a/chiselFrontend/src/main/scala/chisel3/internal/MonoConnect.scala b/chiselFrontend/src/main/scala/chisel3/internal/MonoConnect.scala deleted file mode 100644 index 41402021..00000000 --- a/chiselFrontend/src/main/scala/chisel3/internal/MonoConnect.scala +++ /dev/null @@ -1,264 +0,0 @@ -// See LICENSE for license details. - -package chisel3.internal - -import chisel3._ -import chisel3.experimental.{Analog, BaseModule, EnumType, FixedPoint, Interval, UnsafeEnum} -import chisel3.internal.Builder.pushCommand -import chisel3.internal.firrtl.{Connect, DefInvalid} -import scala.language.experimental.macros -import chisel3.internal.sourceinfo.SourceInfo - -/** -* MonoConnect.connect executes a mono-directional connection element-wise. -* -* Note that this isn't commutative. There is an explicit source and sink -* already determined before this function is called. -* -* The connect operation will recurse down the left Data (with the right Data). -* An exception will be thrown if a movement through the left cannot be matched -* in the right. The right side is allowed to have extra Record fields. -* Vecs must still be exactly the same size. -* -* See elemConnect for details on how the root connections are issued. -* -* Note that a valid sink must be writable so, one of these must hold: -* - Is an internal writable node (Reg or Wire) -* - Is an output of the current module -* - Is an input of a submodule of the current module -* -* Note that a valid source must be readable so, one of these must hold: -* - Is an internal readable node (Reg, Wire, Op) -* - Is a literal -* - Is a port of the current module or submodule of the current module -*/ - -private[chisel3] object MonoConnect { - // scalastyle:off method.name public.methods.have.type - // These are all the possible exceptions that can be thrown. - // These are from element-level connection - def UnreadableSourceException = - MonoConnectException(": Source is unreadable from current module.") - def UnwritableSinkException = - MonoConnectException(": Sink is unwriteable by current module.") - def UnknownRelationException = - MonoConnectException(": Sink or source unavailable to current module.") - // These are when recursing down aggregate types - def MismatchedVecException = - MonoConnectException(": Sink and Source are different length Vecs.") - def MissingFieldException(field: String) = - MonoConnectException(s": Source Record missing field ($field).") - def MismatchedException(sink: String, source: String) = - MonoConnectException(s": Sink ($sink) and Source ($source) have different types.") - def DontCareCantBeSink = - MonoConnectException(": DontCare cannot be a connection sink (LHS)") - def AnalogCantBeMonoSink = - MonoConnectException(": Analog cannot participate in a mono connection (sink - LHS)") - def AnalogCantBeMonoSource = - MonoConnectException(": Analog cannot participate in a mono connection (source - RHS)") - def AnalogMonoConnectionException = - MonoConnectException(": Analog cannot participate in a mono connection (source and sink)") - // scalastyle:on method.name public.methods.have.type - - /** This function is what recursively tries to connect a sink and source together - * - * There is some cleverness in the use of internal try-catch to catch exceptions - * during the recursive decent and then rethrow them with extra information added. - * This gives the user a 'path' to where in the connections things went wrong. - */ - def connect( //scalastyle:off cyclomatic.complexity method.length - sourceInfo: SourceInfo, - connectCompileOptions: CompileOptions, - sink: Data, - source: Data, - context_mod: RawModule): Unit = - (sink, source) match { - - // Handle legal element cases, note (Bool, Bool) is caught by the first two, as Bool is a UInt - case (sink_e: Bool, source_e: UInt) => - elemConnect(sourceInfo, connectCompileOptions, sink_e, source_e, context_mod) - case (sink_e: UInt, source_e: Bool) => - elemConnect(sourceInfo, connectCompileOptions, sink_e, source_e, context_mod) - case (sink_e: UInt, source_e: UInt) => - elemConnect(sourceInfo, connectCompileOptions, sink_e, source_e, context_mod) - case (sink_e: SInt, source_e: SInt) => - elemConnect(sourceInfo, connectCompileOptions, sink_e, source_e, context_mod) - case (sink_e: FixedPoint, source_e: FixedPoint) => - elemConnect(sourceInfo, connectCompileOptions, sink_e, source_e, context_mod) - case (sink_e: Interval, source_e: Interval) => - elemConnect(sourceInfo, connectCompileOptions, sink_e, source_e, context_mod) - case (sink_e: Clock, source_e: Clock) => - elemConnect(sourceInfo, connectCompileOptions, sink_e, source_e, context_mod) - case (sink_e: AsyncReset, source_e: AsyncReset) => - elemConnect(sourceInfo, connectCompileOptions, sink_e, source_e, context_mod) - case (sink_e: ResetType, source_e: Reset) => - elemConnect(sourceInfo, connectCompileOptions, sink_e, source_e, context_mod) - case (sink_e: EnumType, source_e: UnsafeEnum) => - elemConnect(sourceInfo, connectCompileOptions, sink_e, source_e, context_mod) - case (sink_e: EnumType, source_e: EnumType) if sink_e.typeEquivalent(source_e) => - elemConnect(sourceInfo, connectCompileOptions, sink_e, source_e, context_mod) - case (sink_e: UnsafeEnum, source_e: UInt) => - elemConnect(sourceInfo, connectCompileOptions, sink_e, source_e, context_mod) - - // Handle Vec case - case (sink_v: Vec[Data @unchecked], source_v: Vec[Data @unchecked]) => - if(sink_v.length != source_v.length) { throw MismatchedVecException } - for(idx <- 0 until sink_v.length) { - try { - implicit val compileOptions = connectCompileOptions - connect(sourceInfo, connectCompileOptions, sink_v(idx), source_v(idx), context_mod) - } catch { - case MonoConnectException(message) => throw MonoConnectException(s"($idx)$message") - } - } - // Handle Vec connected to DontCare. Apply the DontCare to individual elements. - case (sink_v: Vec[Data @unchecked], DontCare) => - for(idx <- 0 until sink_v.length) { - try { - implicit val compileOptions = connectCompileOptions - connect(sourceInfo, connectCompileOptions, sink_v(idx), source, context_mod) - } catch { - case MonoConnectException(message) => throw MonoConnectException(s"($idx)$message") - } - } - - // Handle Record case - case (sink_r: Record, source_r: Record) => - // For each field, descend with right - for((field, sink_sub) <- sink_r.elements) { - try { - source_r.elements.get(field) match { - case Some(source_sub) => connect(sourceInfo, connectCompileOptions, sink_sub, source_sub, context_mod) - case None => { - if (connectCompileOptions.connectFieldsMustMatch) { - throw MissingFieldException(field) - } - } - } - } catch { - case MonoConnectException(message) => throw MonoConnectException(s".$field$message") - } - } - // Handle Record connected to DontCare. Apply the DontCare to individual elements. - case (sink_r: Record, DontCare) => - // For each field, descend with right - for((field, sink_sub) <- sink_r.elements) { - try { - connect(sourceInfo, connectCompileOptions, sink_sub, source, context_mod) - } catch { - case MonoConnectException(message) => throw MonoConnectException(s".$field$message") - } - } - - // Source is DontCare - it may be connected to anything. It generates a defInvalid for the sink. - case (sink, DontCare) => pushCommand(DefInvalid(sourceInfo, sink.lref)) - // DontCare as a sink is illegal. - case (DontCare, _) => throw DontCareCantBeSink - // Analog is illegal in mono connections. - case (_: Analog, _:Analog) => throw AnalogMonoConnectionException - // Analog is illegal in mono connections. - case (_: Analog, _) => throw AnalogCantBeMonoSink - // Analog is illegal in mono connections. - case (_, _: Analog) => throw AnalogCantBeMonoSource - // Sink and source are different subtypes of data so fail - case (sink, source) => throw MismatchedException(sink.toString, source.toString) - } - - // This function (finally) issues the connection operation - private def issueConnect(sink: Element, source: Element)(implicit sourceInfo: SourceInfo): Unit = { - // If the source is a DontCare, generate a DefInvalid for the sink, - // otherwise, issue a Connect. - source.topBinding match { - case b: DontCareBinding => pushCommand(DefInvalid(sourceInfo, sink.lref)) - case _ => pushCommand(Connect(sourceInfo, sink.lref, source.ref)) - } - } - - // This function checks if element-level connection operation allowed. - // Then it either issues it or throws the appropriate exception. - def elemConnect(implicit sourceInfo: SourceInfo, connectCompileOptions: CompileOptions, sink: Element, source: Element, context_mod: RawModule): Unit = { // scalastyle:ignore line.size.limit - 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.topBinding.location.getOrElse(throw UnwritableSinkException) - val source_mod: BaseModule = source.topBinding.location.getOrElse(context_mod) - - 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 (Output, _) => issueConnect(sink, source) - case (Internal, _) => issueConnect(sink, source) - case (Input, _) => throw UnwritableSinkException - } - } - - // CASE: Context is same module as sink node and right node is in a child module - else if( (sink_mod == context_mod) && - (source_mod._parent.map(_ == context_mod).getOrElse(false)) ) { - // Thus, right node better be a port node and thus have a direction - ((sink_direction, source_direction): @unchecked) match { - // SINK SOURCE - // CURRENT MOD CHILD MOD - 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 (Input, Output) if (!(connectCompileOptions.dontTryConnectionsSwapped)) => issueConnect(source, sink) // scalastyle:ignore line.size.limit - case (Input, _) => throw UnwritableSinkException - } - } - - // CASE: Context is same module as source node and sink node is in child module - else if( (source_mod == context_mod) && - (sink_mod._parent.map(_ == context_mod).getOrElse(false)) ) { - // Thus, left node better be a port node and thus have a direction - ((sink_direction, source_direction): @unchecked) match { - // SINK SOURCE - // CHILD MOD CURRENT MOD - case (Input, _) => issueConnect(sink, source) - case (Output, _) => throw UnwritableSinkException - case (Internal, _) => throw UnwritableSinkException - } - } - - // CASE: Context is the parent module of both the module containing sink node - // and the module containing source node - // Note: This includes case when sink and source in same module but in parent - else if( (sink_mod._parent.map(_ == context_mod).getOrElse(false)) && - (source_mod._parent.map(_ == context_mod).getOrElse(false)) - ) { - // Thus both nodes must be ports and have a direction - ((sink_direction, source_direction): @unchecked) match { - // SINK SOURCE - // CHILD MOD CHILD MOD - 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 (Internal, _) => throw UnwritableSinkException - } - } - - // Not quite sure where left and right are compared to current module - // so just error out - else throw UnknownRelationException - } -} diff --git a/chiselFrontend/src/main/scala/chisel3/internal/Namer.scala b/chiselFrontend/src/main/scala/chisel3/internal/Namer.scala deleted file mode 100644 index 999971a4..00000000 --- a/chiselFrontend/src/main/scala/chisel3/internal/Namer.scala +++ /dev/null @@ -1,154 +0,0 @@ -// See LICENSE for license details. - -// This file contains part of the implementation of the naming static annotation system. - -package chisel3.internal.naming -import chisel3.experimental.NoChiselNamePrefix - -import scala.collection.mutable.Stack -import scala.collection.mutable.ListBuffer - -import scala.collection.JavaConversions._ - -import java.util.IdentityHashMap - -/** Recursive Function Namer overview - * - * In every function, creates a NamingContext object, which associates all vals with a string name - * suffix, for example: - * val myValName = SomeStatement() - * produces the entry in items: - * {ref of SomeStatement(), "myValName"} - * - * This is achieved with a macro transforming: - * val myValName = SomeStatement() - * statements into a naming call: - * val myValName = context.name(SomeStatement(), "myValName") - * - * The context is created from a global dynamic context stack at the beginning of each function. - * At the end of each function call, the completed context is added to its parent context and - * associated with the return value (whose name at an enclosing function call will form the prefix - * for all named objects). - * - * When the naming context prefix is given, it will name all of its items with the prefix and the - * associated suffix name. Then, it will check its descendants for sub-contexts with references - * matching the item reference, and if there is a match, it will (recursively) give the - * sub-context a prefix of its current prefix plus the item reference suffix. - * - * Note that for Modules, the macro will insert a naming context prefix call with an empty prefix, - * starting the recursive naming process. - */ - -/** Base class for naming contexts, providing the basic API consisting of naming calls and - * ability to take descendant naming contexts. - */ -sealed trait NamingContextInterface { - /** Suggest a name (that will be propagated to FIRRTL) for an object, then returns the object - * itself (so this can be inserted transparently anywhere). - * Is a no-op (so safe) when applied on objects that aren't named, including non-Chisel data - * types. - */ - def name[T](obj: T, name: String): T - - /** Gives this context a naming prefix (which may be empty, "", for a top-level Module context) - * so that actual naming calls (HasId.suggestName) can happen. - * Recursively names descendants, for those whose return value have an associated name. - */ - def namePrefix(prefix: String) -} - -/** Dummy implementation to allow for naming annotations in a non-Builder context. - */ -object DummyNamer extends NamingContextInterface { - def name[T](obj: T, name: String): T = obj - - def namePrefix(prefix: String): Unit = { - } -} - -/** Actual namer functionality. - */ -class NamingContext extends NamingContextInterface { - val descendants = new IdentityHashMap[AnyRef, ListBuffer[NamingContext]]() - val anonymousDescendants = ListBuffer[NamingContext]() - val items = ListBuffer[(AnyRef, String)]() - var closed = false // a sanity check to ensure no more name() calls are done after name_prefix - - /** Adds a NamingContext object as a descendant - where its contained objects will have names - * prefixed with the name given to the reference object, if the reference object is named in the - * scope of this context. - */ - def addDescendant(ref: Any, descendant: NamingContext) { - ref match { - case ref: AnyRef => - descendants.getOrElseUpdate(ref, ListBuffer[NamingContext]()) += descendant - case _ => anonymousDescendants += descendant - } - } - - def name[T](obj: T, name: String): T = { - assert(!closed, "Can't name elements after name_prefix called") - obj match { - case _: NoChiselNamePrefix => // Don't name things with NoChiselNamePrefix - case ref: AnyRef => items += ((ref, name)) - case _ => - } - obj - } - - def namePrefix(prefix: String): Unit = { - closed = true - for ((ref, suffix) <- items) { - // First name the top-level object - chisel3.internal.Builder.nameRecursively(prefix + suffix, ref, (id, name) => id.suggestName(name)) - - // Then recurse into descendant contexts - if (descendants.containsKey(ref)) { - for (descendant <- descendants.get(ref)) { - descendant.namePrefix(prefix + suffix + "_") - } - descendants.remove(ref) - } - } - - for (descendant <- descendants.values().flatten) { - // Where we have a broken naming link, just ignore the missing parts - descendant.namePrefix(prefix) - } - for (descendant <- anonymousDescendants) { - descendant.namePrefix(prefix) - } - } -} - -/** Class for the (global) naming stack object, which provides a way to push and pop naming - * contexts as functions are called / finished. - */ -class NamingStack { - val namingStack = Stack[NamingContext]() - - /** Creates a new naming context, where all items in the context will have their names prefixed - * with some yet-to-be-determined prefix from object names in an enclosing scope. - */ - def pushContext(): NamingContext = { - val context = new NamingContext - namingStack.push(context) - context - } - - /** Called at the end of a function, popping the current naming context, adding it to the - * enclosing context's descendants, and passing through the prefix naming reference. - * Every instance of push_context() must have a matching pop_context(). - * - * Will assert out if the context being popped isn't the topmost on the stack. - */ - def popContext[T <: Any](prefixRef: T, until: NamingContext): Unit = { - assert(namingStack.top == until) - namingStack.pop() - if (!namingStack.isEmpty) { - namingStack.top.addDescendant(prefixRef, until) - } - } - - def length() : Int = namingStack.length -} diff --git a/chiselFrontend/src/main/scala/chisel3/internal/SourceInfo.scala b/chiselFrontend/src/main/scala/chisel3/internal/SourceInfo.scala deleted file mode 100644 index f1130db4..00000000 --- a/chiselFrontend/src/main/scala/chisel3/internal/SourceInfo.scala +++ /dev/null @@ -1,61 +0,0 @@ -// See LICENSE for license details. - -// This file contains macros for adding source locators at the point of invocation. -// -// This is not part of coreMacros to disallow this macro from being implicitly invoked in Chisel -// frontend (and generating source locators in Chisel core), which is almost certainly a bug. -// -// Note: While these functions and definitions are not private (macros can't be -// private), these are NOT meant to be part of the public API (yet) and no -// forward compatibility guarantees are made. -// A future revision may stabilize the source locator API to allow library -// writers to append source locator information at the point of a library -// function invocation. - -package chisel3.internal.sourceinfo - -import scala.language.experimental.macros -import scala.reflect.macros.blackbox.Context - -/** Abstract base class for generalized source information. - */ -sealed trait SourceInfo { - /** A prettier toString - * - * Make a useful message if SourceInfo is available, nothing otherwise - */ - def makeMessage(f: String => String): String -} - -sealed trait NoSourceInfo extends SourceInfo { - def makeMessage(f: String => String): String = "" -} - -/** For when source info can't be generated because of a technical limitation, like for Reg because - * Scala macros don't support named or default arguments. - */ -case object UnlocatableSourceInfo extends NoSourceInfo - -/** For when source info isn't generated because the function is deprecated and we're lazy. - */ -case object DeprecatedSourceInfo extends NoSourceInfo - -/** For FIRRTL lines from a Scala source line. - */ -case class SourceLine(filename: String, line: Int, col: Int) extends SourceInfo { - def makeMessage(f: String => String): String = f(s"@[$filename $line:$col]") -} - -/** Provides a macro that returns the source information at the invocation point. - */ -object SourceInfoMacro { - def generate_source_info(c: Context): c.Tree = { - import c.universe._ - val p = c.enclosingPosition - q"_root_.chisel3.internal.sourceinfo.SourceLine(${p.source.file.name}, ${p.line}, ${p.column})" - } -} - -object SourceInfo { - implicit def materialize: SourceInfo = macro SourceInfoMacro.generate_source_info -} diff --git a/chiselFrontend/src/main/scala/chisel3/internal/firrtl/Converter.scala b/chiselFrontend/src/main/scala/chisel3/internal/firrtl/Converter.scala deleted file mode 100644 index 5c1d6935..00000000 --- a/chiselFrontend/src/main/scala/chisel3/internal/firrtl/Converter.scala +++ /dev/null @@ -1,275 +0,0 @@ -// See LICENSE for license details. - -package chisel3.internal.firrtl -import chisel3._ -import chisel3.experimental._ -import chisel3.internal.sourceinfo.{NoSourceInfo, SourceLine, SourceInfo} -import firrtl.{ir => fir} -import chisel3.internal.{castToInt, throwException} - -import scala.annotation.tailrec -import scala.collection.immutable.Queue - -private[chisel3] object Converter { - // TODO modeled on unpack method on Printable, refactor? - def unpack(pable: Printable, ctx: Component): (String, Seq[Arg]) = pable match { - case Printables(pables) => - val (fmts, args) = pables.map(p => unpack(p, ctx)).unzip - (fmts.mkString, args.flatten.toSeq) - case PString(str) => (str.replaceAll("%", "%%"), List.empty) - case format: FirrtlFormat => - ("%" + format.specifier, List(format.bits.ref)) - case Name(data) => (data.ref.name, List.empty) - case FullName(data) => (data.ref.fullName(ctx), List.empty) - case Percent => ("%%", List.empty) - } - - def convert(info: SourceInfo): fir.Info = info match { - case _: NoSourceInfo => fir.NoInfo - case SourceLine(fn, line, col) => fir.FileInfo(fir.StringLit(s"$fn $line:$col")) - } - - def convert(op: PrimOp): fir.PrimOp = firrtl.PrimOps.fromString(op.name) - - def convert(dir: MemPortDirection): firrtl.MPortDir = dir match { - case MemPortDirection.INFER => firrtl.MInfer - case MemPortDirection.READ => firrtl.MRead - case MemPortDirection.WRITE => firrtl.MWrite - case MemPortDirection.RDWR => firrtl.MReadWrite - } - - // TODO - // * Memoize? - // * Move into the Chisel IR? - def convert(arg: Arg, ctx: Component): fir.Expression = arg match { // scalastyle:ignore cyclomatic.complexity - case Node(id) => - convert(id.getRef, ctx) - case Ref(name) => - fir.Reference(name, fir.UnknownType) - case Slot(imm, name) => - fir.SubField(convert(imm, ctx), name, fir.UnknownType) - case Index(imm, ILit(idx)) => - fir.SubIndex(convert(imm, ctx), castToInt(idx, "Index"), fir.UnknownType) - case Index(imm, value) => - fir.SubAccess(convert(imm, ctx), convert(value, ctx), fir.UnknownType) - case ModuleIO(mod, name) => - // scalastyle:off if.brace - if (mod eq ctx.id) fir.Reference(name, fir.UnknownType) - else fir.SubField(fir.Reference(mod.getRef.name, fir.UnknownType), name, fir.UnknownType) - // scalastyle:on if.brace - case u @ ULit(n, UnknownWidth()) => - fir.UIntLiteral(n, fir.IntWidth(u.minWidth)) - case ULit(n, w) => - fir.UIntLiteral(n, convert(w)) - case slit @ SLit(n, w) => fir.SIntLiteral(n, convert(w)) - val unsigned = if (n < 0) (BigInt(1) << slit.width.get) + n else n - val uint = convert(ULit(unsigned, slit.width), ctx) - fir.DoPrim(firrtl.PrimOps.AsSInt, Seq(uint), Seq.empty, fir.UnknownType) - // TODO Simplify - case fplit @ FPLit(n, w, bp) => - val unsigned = if (n < 0) (BigInt(1) << fplit.width.get) + n else n - val uint = convert(ULit(unsigned, fplit.width), ctx) - val lit = bp.asInstanceOf[KnownBinaryPoint].value - fir.DoPrim(firrtl.PrimOps.AsFixedPoint, Seq(uint), Seq(lit), fir.UnknownType) - case intervalLit @ IntervalLit(n, w, bp) => - val unsigned = if (n < 0) (BigInt(1) << intervalLit.width.get) + n else n - val uint = convert(ULit(unsigned, intervalLit.width), ctx) - val lit = bp.asInstanceOf[KnownBinaryPoint].value - fir.DoPrim(firrtl.PrimOps.AsInterval, Seq(uint), Seq(n, n, lit), fir.UnknownType) - case lit: ILit => - throwException(s"Internal Error! Unexpected ILit: $lit") - } - - /** Convert Commands that map 1:1 to Statements */ - def convertSimpleCommand(cmd: Command, ctx: Component): Option[fir.Statement] = cmd match { // scalastyle:ignore cyclomatic.complexity line.size.limit - case e: DefPrim[_] => - val consts = e.args.collect { case ILit(i) => i } - val args = e.args.flatMap { - case _: ILit => None - case other => Some(convert(other, ctx)) - } - val expr = e.op.name match { - case "mux" => - assert(args.size == 3, s"Mux with unexpected args: $args") - fir.Mux(args(0), args(1), args(2), fir.UnknownType) - case _ => - fir.DoPrim(convert(e.op), args, consts, fir.UnknownType) - } - Some(fir.DefNode(convert(e.sourceInfo), e.name, expr)) - case e @ DefWire(info, id) => - Some(fir.DefWire(convert(info), e.name, extractType(id))) - case e @ DefReg(info, id, clock) => - Some(fir.DefRegister(convert(info), e.name, extractType(id), convert(clock, ctx), - firrtl.Utils.zero, convert(id.getRef, ctx))) - case e @ DefRegInit(info, id, clock, reset, init) => - Some(fir.DefRegister(convert(info), e.name, extractType(id), convert(clock, ctx), - convert(reset, ctx), convert(init, ctx))) - case e @ DefMemory(info, id, t, size) => - Some(firrtl.CDefMemory(convert(info), e.name, extractType(t), size, false)) - case e @ DefSeqMemory(info, id, t, size, ruw) => - Some(firrtl.CDefMemory(convert(info), e.name, extractType(t), size, true, ruw)) - case e: DefMemPort[_] => - Some(firrtl.CDefMPort(convert(e.sourceInfo), e.name, fir.UnknownType, - e.source.fullName(ctx), Seq(convert(e.index, ctx), convert(e.clock, ctx)), convert(e.dir))) - case Connect(info, loc, exp) => - Some(fir.Connect(convert(info), convert(loc, ctx), convert(exp, ctx))) - case BulkConnect(info, loc, exp) => - Some(fir.PartialConnect(convert(info), convert(loc, ctx), convert(exp, ctx))) - case Attach(info, locs) => - Some(fir.Attach(convert(info), locs.map(l => convert(l, ctx)))) - case DefInvalid(info, arg) => - Some(fir.IsInvalid(convert(info), convert(arg, ctx))) - case e @ DefInstance(info, id, _) => - Some(fir.DefInstance(convert(info), e.name, id.name)) - case Stop(info, clock, ret) => - Some(fir.Stop(convert(info), ret, convert(clock, ctx), firrtl.Utils.one)) - case Printf(info, clock, pable) => - val (fmt, args) = unpack(pable, ctx) - Some(fir.Print(convert(info), fir.StringLit(fmt), - args.map(a => convert(a, ctx)), convert(clock, ctx), firrtl.Utils.one)) - case _ => None - } - - /** Internal datastructure to help translate Chisel's flat Command structure to FIRRTL's AST - * - * In particular, when scoping is translated from flat with begin end to a nested datastructure - * - * @param when Current when Statement, holds info, condition, and consequence as they are - * available - * @param outer Already converted Statements that precede the current when block in the scope in - * which the when is defined (ie. 1 level up from the scope inside the when) - * @param alt Indicates if currently processing commands in the alternate (else) of the when scope - */ - // TODO we should probably have a different structure in the IR to close elses - private case class WhenFrame(when: fir.Conditionally, outer: Queue[fir.Statement], alt: Boolean) - - /** Convert Chisel IR Commands into FIRRTL Statements - * - * @note ctx is needed because references to ports translate differently when referenced within - * the module in which they are defined vs. parent modules - * @param cmds Chisel IR Commands to convert - * @param ctx Component (Module) context within which we are translating - * @return FIRRTL Statement that is equivalent to the input cmds - */ - def convert(cmds: Seq[Command], ctx: Component): fir.Statement = { // scalastyle:ignore cyclomatic.complexity - @tailrec - // scalastyle:off if.brace - def rec(acc: Queue[fir.Statement], - scope: List[WhenFrame]) - (cmds: Seq[Command]): Seq[fir.Statement] = { - if (cmds.isEmpty) { - assert(scope.isEmpty) - acc - } else convertSimpleCommand(cmds.head, ctx) match { - // Most Commands map 1:1 - case Some(stmt) => - rec(acc :+ stmt, scope)(cmds.tail) - // When scoping logic does not map 1:1 and requires pushing/popping WhenFrames - // Please see WhenFrame for more details - case None => cmds.head match { - case WhenBegin(info, pred) => - val when = fir.Conditionally(convert(info), convert(pred, ctx), fir.EmptyStmt, fir.EmptyStmt) - val frame = WhenFrame(when, acc, false) - rec(Queue.empty, frame +: scope)(cmds.tail) - case WhenEnd(info, depth, _) => - val frame = scope.head - val when = if (frame.alt) frame.when.copy(alt = fir.Block(acc)) - else frame.when.copy(conseq = fir.Block(acc)) - // Check if this when has an else - cmds.tail.headOption match { - case Some(AltBegin(_)) => - assert(!frame.alt, "Internal Error! Unexpected when structure!") // Only 1 else per when - rec(Queue.empty, frame.copy(when = when, alt = true) +: scope.tail)(cmds.drop(2)) - case _ => // Not followed by otherwise - // If depth > 0 then we need to close multiple When scopes so we add a new WhenEnd - // If we're nested we need to add more WhenEnds to ensure each When scope gets - // properly closed - val cmdsx = if (depth > 0) WhenEnd(info, depth - 1, false) +: cmds.tail else cmds.tail - rec(frame.outer :+ when, scope.tail)(cmdsx) - } - case OtherwiseEnd(info, depth) => - val frame = scope.head - val when = frame.when.copy(alt = fir.Block(acc)) - // TODO For some reason depth == 1 indicates the last closing otherwise whereas - // depth == 0 indicates last closing when - val cmdsx = if (depth > 1) OtherwiseEnd(info, depth - 1) +: cmds.tail else cmds.tail - rec(scope.head.outer :+ when, scope.tail)(cmdsx) - } - } - } - // scalastyle:on if.brace - fir.Block(rec(Queue.empty, List.empty)(cmds)) - } - - def convert(width: Width): fir.Width = width match { - case UnknownWidth() => fir.UnknownWidth - case KnownWidth(value) => fir.IntWidth(value) - } - - def convert(bp: BinaryPoint): fir.Width = bp match { - case UnknownBinaryPoint => fir.UnknownWidth - case KnownBinaryPoint(value) => fir.IntWidth(value) - } - - private def firrtlUserDirOf(d: Data): SpecifiedDirection = d match { - case d: Vec[_] => - SpecifiedDirection.fromParent(d.specifiedDirection, firrtlUserDirOf(d.sample_element)) - case d => d.specifiedDirection - } - - def extractType(data: Data, clearDir: Boolean = false): fir.Type = data match { // scalastyle:ignore cyclomatic.complexity line.size.limit - case _: Clock => fir.ClockType - case _: AsyncReset => fir.AsyncResetType - case _: ResetType => fir.ResetType - case d: EnumType => fir.UIntType(convert(d.width)) - case d: UInt => fir.UIntType(convert(d.width)) - case d: SInt => fir.SIntType(convert(d.width)) - case d: FixedPoint => fir.FixedType(convert(d.width), convert(d.binaryPoint)) - case d: Interval => fir.IntervalType(d.range.lowerBound, d.range.upperBound, d.range.firrtlBinaryPoint) - case d: Analog => fir.AnalogType(convert(d.width)) - case d: Vec[_] => fir.VectorType(extractType(d.sample_element, clearDir), d.length) - case d: Record => - val childClearDir = clearDir || - d.specifiedDirection == SpecifiedDirection.Input || d.specifiedDirection == SpecifiedDirection.Output - def eltField(elt: Data): fir.Field = (childClearDir, firrtlUserDirOf(elt)) match { - case (true, _) => fir.Field(elt.getRef.name, fir.Default, extractType(elt, true)) - case (false, SpecifiedDirection.Unspecified | SpecifiedDirection.Output) => - fir.Field(elt.getRef.name, fir.Default, extractType(elt, false)) - case (false, SpecifiedDirection.Flip | SpecifiedDirection.Input) => - fir.Field(elt.getRef.name, fir.Flip, extractType(elt, false)) - } - fir.BundleType(d.elements.toIndexedSeq.reverse.map { case (_, e) => eltField(e) }) - } - - def convert(name: String, param: Param): fir.Param = param match { - case IntParam(value) => fir.IntParam(name, value) - case DoubleParam(value) => fir.DoubleParam(name, value) - case StringParam(value) => fir.StringParam(name, fir.StringLit(value)) - case RawParam(value) => fir.RawStringParam(name, value) - } - def convert(port: Port, topDir: SpecifiedDirection = SpecifiedDirection.Unspecified): fir.Port = { - val resolvedDir = SpecifiedDirection.fromParent(topDir, port.dir) - val dir = resolvedDir match { - case SpecifiedDirection.Unspecified | SpecifiedDirection.Output => fir.Output - case SpecifiedDirection.Flip | SpecifiedDirection.Input => fir.Input - } - val clearDir = resolvedDir match { - case SpecifiedDirection.Input | SpecifiedDirection.Output => true - case SpecifiedDirection.Unspecified | SpecifiedDirection.Flip => false - } - val tpe = extractType(port.id, clearDir) - fir.Port(fir.NoInfo, port.id.getRef.name, dir, tpe) - } - - def convert(component: Component): fir.DefModule = component match { - case ctx @ DefModule(_, name, ports, cmds) => - fir.Module(fir.NoInfo, name, ports.map(p => convert(p)), convert(cmds.toList, ctx)) - case ctx @ DefBlackBox(id, name, ports, topDir, params) => - fir.ExtModule(fir.NoInfo, name, ports.map(p => convert(p, topDir)), id.desiredName, - params.map { case (name, p) => convert(name, p) }.toSeq) - } - - def convert(circuit: Circuit): fir.Circuit = - fir.Circuit(fir.NoInfo, circuit.components.map(convert), circuit.name) -} - diff --git a/chiselFrontend/src/main/scala/chisel3/internal/firrtl/IR.scala b/chiselFrontend/src/main/scala/chisel3/internal/firrtl/IR.scala deleted file mode 100644 index d98bebcd..00000000 --- a/chiselFrontend/src/main/scala/chisel3/internal/firrtl/IR.scala +++ /dev/null @@ -1,750 +0,0 @@ -// See LICENSE for license details. - -package chisel3.internal.firrtl - -import firrtl.{ir => fir} - -import chisel3._ -import chisel3.internal._ -import chisel3.internal.sourceinfo.SourceInfo -import chisel3.experimental._ -import _root_.firrtl.{ir => firrtlir} -import _root_.firrtl.PrimOps - -import scala.collection.immutable.NumericRange -import scala.math.BigDecimal.RoundingMode - -// scalastyle:off number.of.types - -case class PrimOp(name: String) { - override def toString: String = name -} - -object PrimOp { - val AddOp = PrimOp("add") - val SubOp = PrimOp("sub") - val TailOp = PrimOp("tail") - val HeadOp = PrimOp("head") - val TimesOp = PrimOp("mul") - val DivideOp = PrimOp("div") - val RemOp = PrimOp("rem") - val ShiftLeftOp = PrimOp("shl") - val ShiftRightOp = PrimOp("shr") - val DynamicShiftLeftOp = PrimOp("dshl") - val DynamicShiftRightOp = PrimOp("dshr") - val BitAndOp = PrimOp("and") - val BitOrOp = PrimOp("or") - val BitXorOp = PrimOp("xor") - val BitNotOp = PrimOp("not") - val ConcatOp = PrimOp("cat") - val BitsExtractOp = PrimOp("bits") - val LessOp = PrimOp("lt") - val LessEqOp = PrimOp("leq") - val GreaterOp = PrimOp("gt") - val GreaterEqOp = PrimOp("geq") - val EqualOp = PrimOp("eq") - val PadOp = PrimOp("pad") - val NotEqualOp = PrimOp("neq") - val NegOp = PrimOp("neg") - val MultiplexOp = PrimOp("mux") - val AndReduceOp = PrimOp("andr") - val OrReduceOp = PrimOp("orr") - val XorReduceOp = PrimOp("xorr") - val ConvertOp = PrimOp("cvt") - val AsUIntOp = PrimOp("asUInt") - val AsSIntOp = PrimOp("asSInt") - val AsFixedPointOp = PrimOp("asFixedPoint") - val AsIntervalOp = PrimOp("asInterval") - val WrapOp = PrimOp("wrap") - val SqueezeOp = PrimOp("squz") - val ClipOp = PrimOp("clip") - val SetBinaryPoint = PrimOp("setp") - val IncreasePrecision = PrimOp("incp") - val DecreasePrecision = PrimOp("decp") - val AsClockOp = PrimOp("asClock") - val AsAsyncResetOp = PrimOp("asAsyncReset") -} - -abstract class Arg { - def fullName(ctx: Component): String = name - def name: String -} - -case class Node(id: HasId) extends Arg { - override def fullName(ctx: Component): String = id.getOptionRef match { - case Some(arg) => arg.fullName(ctx) - case None => id.suggestedName.getOrElse("??") - } - def name: String = id.getOptionRef match { - case Some(arg) => arg.name - case None => id.suggestedName.getOrElse("??") - } -} - -abstract class LitArg(val num: BigInt, widthArg: Width) extends Arg { - private[chisel3] def forcedWidth = widthArg.known - private[chisel3] def width: Width = if (forcedWidth) widthArg else Width(minWidth) - override def fullName(ctx: Component): String = name - // Ensure the node representing this LitArg has a ref to it and a literal binding. - def bindLitArg[T <: Element](elem: T): T = { - elem.bind(ElementLitBinding(this)) - elem.setRef(this) - elem - } - - protected def minWidth: Int - if (forcedWidth) { - require(widthArg.get >= minWidth, - s"The literal value ${num} was elaborated with a specified width of ${widthArg.get} bits, but at least ${minWidth} bits are required.") // scalastyle:ignore line.size.limit - } -} - -case class ILit(n: BigInt) extends Arg { - def name: String = n.toString -} - -case class ULit(n: BigInt, w: Width) extends LitArg(n, w) { - def name: String = "UInt" + width + "(\"h0" + num.toString(16) + "\")" - def minWidth: Int = 1 max n.bitLength - - require(n >= 0, s"UInt literal ${n} is negative") -} - -case class SLit(n: BigInt, w: Width) extends LitArg(n, w) { - def name: String = { - val unsigned = if (n < 0) (BigInt(1) << width.get) + n else n - s"asSInt(${ULit(unsigned, width).name})" - } - def minWidth: Int = 1 + n.bitLength -} - -case class FPLit(n: BigInt, w: Width, binaryPoint: BinaryPoint) extends LitArg(n, w) { - def name: String = { - val unsigned = if (n < 0) (BigInt(1) << width.get) + n else n - s"asFixedPoint(${ULit(unsigned, width).name}, ${binaryPoint.asInstanceOf[KnownBinaryPoint].value})" - } - def minWidth: Int = 1 + n.bitLength -} - -case class IntervalLit(n: BigInt, w: Width, binaryPoint: BinaryPoint) extends LitArg(n, w) { - def name: String = { - val unsigned = if (n < 0) (BigInt(1) << width.get) + n else n - s"asInterval(${ULit(unsigned, width).name}, ${n}, ${n}, ${binaryPoint.asInstanceOf[KnownBinaryPoint].value})" - } - val range: IntervalRange = { - new IntervalRange(IntervalRange.getBound(isClosed = true, BigDecimal(n)), - IntervalRange.getBound(isClosed = true, BigDecimal(n)), IntervalRange.getRangeWidth(binaryPoint)) - } - def minWidth: Int = 1 + n.bitLength -} - -case class Ref(name: String) extends Arg -case class ModuleIO(mod: BaseModule, name: String) extends Arg { - override def fullName(ctx: Component): String = - if (mod eq ctx.id) name else s"${mod.getRef.name}.$name" -} -case class Slot(imm: Node, name: String) extends Arg { - override def fullName(ctx: Component): String = - if (imm.fullName(ctx).isEmpty) name else s"${imm.fullName(ctx)}.${name}" -} -case class Index(imm: Arg, value: Arg) extends Arg { - def name: String = s"[$value]" - override def fullName(ctx: Component): String = s"${imm.fullName(ctx)}[${value.fullName(ctx)}]" -} - -object Width { - def apply(x: Int): Width = KnownWidth(x) - def apply(): Width = UnknownWidth() -} - -sealed abstract class Width { - type W = Int - def max(that: Width): Width = this.op(that, _ max _) - def + (that: Width): Width = this.op(that, _ + _) - def + (that: Int): Width = this.op(this, (a, b) => a + that) - def shiftRight(that: Int): Width = this.op(this, (a, b) => 0 max (a - that)) - def dynamicShiftLeft(that: Width): Width = - this.op(that, (a, b) => a + (1 << b) - 1) - - def known: Boolean - def get: W - protected def op(that: Width, f: (W, W) => W): Width -} - -sealed case class UnknownWidth() extends Width { - def known: Boolean = false - def get: Int = None.get - def op(that: Width, f: (W, W) => W): Width = this - override def toString: String = "" -} - -sealed case class KnownWidth(value: Int) extends Width { - require(value >= 0) - def known: Boolean = true - def get: Int = value - def op(that: Width, f: (W, W) => W): Width = that match { - case KnownWidth(x) => KnownWidth(f(value, x)) - case _ => that - } - override def toString: String = s"<${value.toString}>" -} - -object BinaryPoint { - def apply(x: Int): BinaryPoint = KnownBinaryPoint(x) - def apply(): BinaryPoint = UnknownBinaryPoint -} - -sealed abstract class BinaryPoint { - type W = Int - def max(that: BinaryPoint): BinaryPoint = this.op(that, _ max _) - def + (that: BinaryPoint): BinaryPoint = this.op(that, _ + _) - def + (that: Int): BinaryPoint = this.op(this, (a, b) => a + that) - def shiftRight(that: Int): BinaryPoint = this.op(this, (a, b) => 0 max (a - that)) - def dynamicShiftLeft(that: BinaryPoint): BinaryPoint = - this.op(that, (a, b) => a + (1 << b) - 1) - - def known: Boolean - def get: W - protected def op(that: BinaryPoint, f: (W, W) => W): BinaryPoint -} - -case object UnknownBinaryPoint extends BinaryPoint { - def known: Boolean = false - def get: Int = None.get - def op(that: BinaryPoint, f: (W, W) => W): BinaryPoint = this - override def toString: String = "" -} - -sealed case class KnownBinaryPoint(value: Int) extends BinaryPoint { - def known: Boolean = true - def get: Int = value - def op(that: BinaryPoint, f: (W, W) => W): BinaryPoint = that match { - case KnownBinaryPoint(x) => KnownBinaryPoint(f(value, x)) - case _ => that - } - override def toString: String = s"<<${value.toString}>>" -} - - -sealed abstract class MemPortDirection(name: String) { - override def toString: String = name -} -object MemPortDirection { - object READ extends MemPortDirection("read") - object WRITE extends MemPortDirection("write") - object RDWR extends MemPortDirection("rdwr") - object INFER extends MemPortDirection("infer") -} - -sealed trait RangeType { - def getWidth: Width - - def * (that: IntervalRange): IntervalRange - def +& (that: IntervalRange): IntervalRange - def -& (that: IntervalRange): IntervalRange - def << (that: Int): IntervalRange - def >> (that: Int): IntervalRange - def << (that: KnownWidth): IntervalRange - def >> (that: KnownWidth): IntervalRange - def merge(that: IntervalRange): IntervalRange -} - -object IntervalRange { - /** Creates an IntervalRange, this is used primarily by the range interpolator macro - * @param lower lower bound - * @param upper upper bound - * @param firrtlBinaryPoint binary point firrtl style - * @return - */ - def apply(lower: firrtlir.Bound, upper: firrtlir.Bound, firrtlBinaryPoint: firrtlir.Width): IntervalRange = { - new IntervalRange(lower, upper, firrtlBinaryPoint) - } - - def apply(lower: firrtlir.Bound, upper: firrtlir.Bound, binaryPoint: BinaryPoint): IntervalRange = { - new IntervalRange(lower, upper, IntervalRange.getBinaryPoint(binaryPoint)) - } - - def apply(lower: firrtlir.Bound, upper: firrtlir.Bound, binaryPoint: Int): IntervalRange = { - IntervalRange(lower, upper, BinaryPoint(binaryPoint)) - } - - /** Returns an IntervalRange appropriate for a signed value of the given width - * @param binaryPoint number of bits of mantissa - * @return - */ - def apply(binaryPoint: BinaryPoint): IntervalRange = { - IntervalRange(firrtlir.UnknownBound, firrtlir.UnknownBound, binaryPoint) - } - - /** Returns an IntervalRange appropriate for a signed value of the given width - * @param width number of bits to have in the interval - * @param binaryPoint number of bits of mantissa - * @return - */ - def apply(width: Width, binaryPoint: BinaryPoint = 0.BP): IntervalRange = { - val range = width match { - case KnownWidth(w) => - val nearestPowerOf2 = BigInt("1" + ("0" * (w - 1)), 2) - IntervalRange( - firrtlir.Closed(BigDecimal(-nearestPowerOf2)), firrtlir.Closed(BigDecimal(nearestPowerOf2 - 1)), binaryPoint - ) - case _ => - IntervalRange(firrtlir.UnknownBound, firrtlir.UnknownBound, binaryPoint) - } - range - } - - def unapply(arg: IntervalRange): Option[(firrtlir.Bound, firrtlir.Bound, BinaryPoint)] = { - return Some((arg.lower, arg.upper, arg.binaryPoint)) - } - - def getBound(isClosed: Boolean, value: String): firrtlir.Bound = { - if(value == "?") { - firrtlir.UnknownBound - } - else if(isClosed) { - firrtlir.Closed(BigDecimal(value)) - } - else { - firrtlir.Open(BigDecimal(value)) - } - } - - def getBound(isClosed: Boolean, value: BigDecimal): firrtlir.Bound = { - if(isClosed) { - firrtlir.Closed(value) - } - else { - firrtlir.Open(value) - } - } - - def getBound(isClosed: Boolean, value: Int): firrtlir.Bound = { - getBound(isClosed, (BigDecimal(value))) - } - - def getBinaryPoint(s: String): firrtlir.Width = { - firrtlir.UnknownWidth - } - - def getBinaryPoint(n: Int): firrtlir.Width = { - if(n < 0) { - firrtlir.UnknownWidth - } - else { - firrtlir.IntWidth(n) - } - } - def getBinaryPoint(n: BinaryPoint): firrtlir.Width = { - n match { - case UnknownBinaryPoint => firrtlir.UnknownWidth - case KnownBinaryPoint(w) => firrtlir.IntWidth(w) - } - } - - def getRangeWidth(w: Width): firrtlir.Width = { - if(w.known) { - firrtlir.IntWidth(w.get) - } - else { - firrtlir.UnknownWidth - } - } - def getRangeWidth(binaryPoint: BinaryPoint): firrtlir.Width = { - if(binaryPoint.known) { - firrtlir.IntWidth(binaryPoint.get) - } - else { - firrtlir.UnknownWidth - } - } - - //scalastyle:off method.name - def Unknown: IntervalRange = range"[?,?].?" -} - - -sealed class IntervalRange( - val lowerBound: firrtlir.Bound, - val upperBound: firrtlir.Bound, - private[chisel3] val firrtlBinaryPoint: firrtlir.Width) - extends firrtlir.IntervalType(lowerBound, upperBound, firrtlBinaryPoint) - with RangeType { - - (lowerBound, upperBound) match { - case (firrtlir.Open(begin), firrtlir.Open(end)) => - if(begin >= end) throw new ChiselException(s"Invalid range with ${serialize}") - binaryPoint match { - case KnownBinaryPoint(bp) => - if(begin >= end - (BigDecimal(1) / BigDecimal(BigInt(1) << bp))) { - throw new ChiselException(s"Invalid range with ${serialize}") - } - case _ => - } - case (firrtlir.Open(begin), firrtlir.Closed(end)) => - if(begin >= end) throw new ChiselException(s"Invalid range with ${serialize}") - case (firrtlir.Closed(begin), firrtlir.Open(end)) => - if(begin >= end) throw new ChiselException(s"Invalid range with ${serialize}") - case (firrtlir.Closed(begin), firrtlir.Closed(end)) => - if(begin > end) throw new ChiselException(s"Invalid range with ${serialize}") - case _ => - } - - //scalastyle:off cyclomatic.complexity - override def toString: String = { - val binaryPoint = firrtlBinaryPoint match { - case firrtlir.IntWidth(n) => s"$n" - case _ => "?" - } - val lowerBoundString = lowerBound match { - case firrtlir.Closed(l) => s"[$l" - case firrtlir.Open(l) => s"($l" - case firrtlir.UnknownBound => s"[?" - } - val upperBoundString = upperBound match { - case firrtlir.Closed(l) => s"$l]" - case firrtlir.Open(l) => s"$l)" - case firrtlir.UnknownBound => s"?]" - } - s"""range"$lowerBoundString,$upperBoundString.$binaryPoint"""" - } - - val increment: Option[BigDecimal] = firrtlBinaryPoint match { - case firrtlir.IntWidth(bp) => - Some(BigDecimal(math.pow(2, -bp.doubleValue))) - case _ => None - } - - /** If possible returns the lowest possible value for this Interval - * @return - */ - val getLowestPossibleValue: Option[BigDecimal] = { - increment match { - case Some(inc) => - lower match { - case firrtlir.Closed(n) => Some(n) - case firrtlir.Open(n) => Some(n + inc) - case _ => None - } - case _ => - None - } - } - - /** If possible returns the highest possible value for this Interval - * @return - */ - val getHighestPossibleValue: Option[BigDecimal] = { - increment match { - case Some(inc) => - upper match { - case firrtlir.Closed(n) => Some(n) - case firrtlir.Open(n) => Some(n - inc) - case _ => None - } - case _ => - None - } - } - - /** Return a Seq of the possible values for this range - * Mostly to be used for testing - * @return - */ - def getPossibleValues: NumericRange[BigDecimal] = { - (getLowestPossibleValue, getHighestPossibleValue, increment) match { - case (Some(low), Some(high), Some(inc)) => (low to high by inc) - case (_, _, None) => - throw new ChiselException(s"BinaryPoint unknown. Cannot get possible values from IntervalRange $toString") - case _ => - throw new ChiselException(s"Unknown Bound. Cannot get possible values from IntervalRange $toString") - - } - } - - override def getWidth: Width = { - width match { - case firrtlir.IntWidth(n) => KnownWidth(n.toInt) - case firrtlir.UnknownWidth => UnknownWidth() - } - } - - private def doFirrtlOp(op: firrtlir.PrimOp, that: IntervalRange): IntervalRange = { - PrimOps.set_primop_type( - firrtlir.DoPrim(op, - Seq(firrtlir.Reference("a", this), firrtlir.Reference("b", that)), Nil,firrtlir.UnknownType) - ).tpe match { - case i: firrtlir.IntervalType => IntervalRange(i.lower, i.upper, i.point) - case other => sys.error("BAD!") - } - } - - private def doFirrtlDynamicShift(that: UInt, isLeft: Boolean): IntervalRange = { - val uinttpe = that.widthOption match { - case None => firrtlir.UIntType(firrtlir.UnknownWidth) - case Some(w) => firrtlir.UIntType(firrtlir.IntWidth(w)) - } - val op = if(isLeft) PrimOps.Dshl else PrimOps.Dshr - PrimOps.set_primop_type( - firrtlir.DoPrim(op, - Seq(firrtlir.Reference("a", this), firrtlir.Reference("b", uinttpe)), Nil,firrtlir.UnknownType) - ).tpe match { - case i: firrtlir.IntervalType => IntervalRange(i.lower, i.upper, i.point) - case other => sys.error("BAD!") - } - } - - private def doFirrtlOp(op: firrtlir.PrimOp, that: Int): IntervalRange = { - PrimOps.set_primop_type( - firrtlir.DoPrim(op, - Seq(firrtlir.Reference("a", this)), Seq(BigInt(that)), firrtlir.UnknownType) - ).tpe match { - case i: firrtlir.IntervalType => IntervalRange(i.lower, i.upper, i.point) - case other => sys.error("BAD!") - } - } - - /** Multiply this by that, here we return a fully unknown range, - * firrtl's range inference can figure this out - * @param that - * @return - */ - override def *(that: IntervalRange): IntervalRange = { - doFirrtlOp(PrimOps.Mul, that) - } - - /** Add that to this, here we return a fully unknown range, - * firrtl's range inference can figure this out - * @param that - * @return - */ - override def +&(that: IntervalRange): IntervalRange = { - doFirrtlOp(PrimOps.Add, that) - } - - /** Subtract that from this, here we return a fully unknown range, - * firrtl's range inference can figure this out - * @param that - * @return - */ - override def -&(that: IntervalRange): IntervalRange = { - doFirrtlOp(PrimOps.Sub, that) - } - - private def adjustBoundValue(value: BigDecimal, binaryPointValue: Int): BigDecimal = { - if(binaryPointValue >= 0) { - val maskFactor = BigDecimal(1 << binaryPointValue) - val a = (value * maskFactor) - val b = a.setScale(0, RoundingMode.DOWN) - val c = b / maskFactor - c - } else { - value - } - } - - private def adjustBound(bound: firrtlir.Bound, binaryPoint: BinaryPoint): firrtlir.Bound = { - binaryPoint match { - case KnownBinaryPoint(binaryPointValue) => - bound match { - case firrtlir.Open(value) => firrtlir.Open(adjustBoundValue(value, binaryPointValue)) - case firrtlir.Closed(value) => firrtlir.Closed(adjustBoundValue(value, binaryPointValue)) - case _ => bound - } - case _ => firrtlir.UnknownBound - } - } - - /** Creates a new range with the increased precision - * - * @param newBinaryPoint - * @return - */ - def incPrecision(newBinaryPoint: BinaryPoint): IntervalRange = { - newBinaryPoint match { - case KnownBinaryPoint(that) => - doFirrtlOp(PrimOps.IncP, that) - case _ => - throwException(s"$this.incPrecision(newBinaryPoint = $newBinaryPoint) error, newBinaryPoint must be know") - } - } - - /** Creates a new range with the decreased precision - * - * @param newBinaryPoint - * @return - */ - def decPrecision(newBinaryPoint: BinaryPoint): IntervalRange = { - newBinaryPoint match { - case KnownBinaryPoint(that) => - doFirrtlOp(PrimOps.DecP, that) - case _ => - throwException(s"$this.decPrecision(newBinaryPoint = $newBinaryPoint) error, newBinaryPoint must be know") - } - } - - /** Creates a new range with the given binary point, adjusting precision - * on bounds as necessary - * - * @param newBinaryPoint - * @return - */ - def setPrecision(newBinaryPoint: BinaryPoint): IntervalRange = { - newBinaryPoint match { - case KnownBinaryPoint(that) => - doFirrtlOp(PrimOps.SetP, that) - case _ => - throwException(s"$this.setPrecision(newBinaryPoint = $newBinaryPoint) error, newBinaryPoint must be know") - } - } - - /** Shift this range left, i.e. shifts the min and max by the specified amount - * @param that - * @return - */ - override def <<(that: Int): IntervalRange = { - doFirrtlOp(PrimOps.Shl, that) - } - - /** Shift this range left, i.e. shifts the min and max by the known width - * @param that - * @return - */ - override def <<(that: KnownWidth): IntervalRange = { - <<(that.value) - } - - /** Shift this range left, i.e. shifts the min and max by value - * @param that - * @return - */ - def <<(that: UInt): IntervalRange = { - doFirrtlDynamicShift(that, isLeft = true) - } - - /** Shift this range right, i.e. shifts the min and max by the specified amount - * @param that - * @return - */ - override def >>(that: Int): IntervalRange = { - doFirrtlOp(PrimOps.Shr, that) - } - - /** Shift this range right, i.e. shifts the min and max by the known width - * @param that - * @return - */ - override def >>(that: KnownWidth): IntervalRange = { - >>(that.value) - } - - /** Shift this range right, i.e. shifts the min and max by value - * @param that - * @return - */ - def >>(that: UInt): IntervalRange = { - doFirrtlDynamicShift(that, isLeft = false) - } - - /** - * Squeeze returns the intersection of the ranges this interval and that Interval - * @param that - * @return - */ - def squeeze(that: IntervalRange): IntervalRange = { - doFirrtlOp(PrimOps.Squeeze, that) - } - - /** - * Wrap the value of this [[Interval]] into the range of a different Interval with a presumably smaller range. - * @param that - * @return - */ - def wrap(that: IntervalRange): IntervalRange = { - doFirrtlOp(PrimOps.Wrap, that) - } - - /** - * Clip the value of this [[Interval]] into the range of a different Interval with a presumably smaller range. - * @param that - * @return - */ - def clip(that: IntervalRange): IntervalRange = { - doFirrtlOp(PrimOps.Clip, that) - } - - /** merges the ranges of this and that, basically takes lowest low, highest high and biggest bp - * set unknown if any of this or that's value of above is unknown - * Like an union but will slurp up points in between the two ranges that were part of neither - * @param that - * @return - */ - override def merge(that: IntervalRange): IntervalRange = { - val lowest = (this.getLowestPossibleValue, that.getLowestPossibleValue) match { - case (Some(l1), Some(l2)) => - if(l1 < l2) { this.lower } else { that.lower } - case _ => - firrtlir.UnknownBound - } - val highest = (this.getHighestPossibleValue, that.getHighestPossibleValue) match { - case (Some(l1), Some(l2)) => - if(l1 >= l2) { this.lower } else { that.lower } - case _ => - firrtlir.UnknownBound - } - val newBinaryPoint = (this.firrtlBinaryPoint, that.firrtlBinaryPoint) match { - case (firrtlir.IntWidth(b1), firrtlir.IntWidth(b2)) => - if(b1 > b2) { firrtlir.IntWidth(b1)} else { firrtlir.IntWidth(b2) } - case _ => - firrtlir.UnknownWidth - } - IntervalRange(lowest, highest, newBinaryPoint) - } - - def binaryPoint: BinaryPoint = { - firrtlBinaryPoint match { - case firrtlir.IntWidth(n) => - assert(n < Int.MaxValue, s"binary point value $n is out of range") - KnownBinaryPoint(n.toInt) - case _ => UnknownBinaryPoint - } - } -} - -abstract class Command { - def sourceInfo: SourceInfo -} -abstract class Definition extends Command { - def id: HasId - def name: String = id.getRef.name -} -// scalastyle:off line.size.limit -case class DefPrim[T <: Data](sourceInfo: SourceInfo, id: T, op: PrimOp, args: Arg*) extends Definition -case class DefInvalid(sourceInfo: SourceInfo, arg: Arg) extends Command -case class DefWire(sourceInfo: SourceInfo, id: Data) extends Definition -case class DefReg(sourceInfo: SourceInfo, id: Data, clock: Arg) extends Definition -case class DefRegInit(sourceInfo: SourceInfo, id: Data, clock: Arg, reset: Arg, init: Arg) extends Definition -case class DefMemory(sourceInfo: SourceInfo, id: HasId, t: Data, size: BigInt) extends Definition -case class DefSeqMemory(sourceInfo: SourceInfo, id: HasId, t: Data, size: BigInt, readUnderWrite: fir.ReadUnderWrite.Value) extends Definition -case class DefMemPort[T <: Data](sourceInfo: SourceInfo, id: T, source: Node, dir: MemPortDirection, index: Arg, clock: Arg) extends Definition -case class DefInstance(sourceInfo: SourceInfo, id: BaseModule, ports: Seq[Port]) extends Definition -case class WhenBegin(sourceInfo: SourceInfo, pred: Arg) extends Command -case class WhenEnd(sourceInfo: SourceInfo, firrtlDepth: Int, hasAlt: Boolean = false) extends Command -case class AltBegin(sourceInfo: SourceInfo) extends Command -case class OtherwiseEnd(sourceInfo: SourceInfo, firrtlDepth: Int) extends Command -case class Connect(sourceInfo: SourceInfo, loc: Node, exp: Arg) extends Command -case class BulkConnect(sourceInfo: SourceInfo, loc1: Node, loc2: Node) extends Command -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: SpecifiedDirection) -case class Printf(sourceInfo: SourceInfo, clock: Arg, pable: Printable) extends Command -abstract class Component extends Arg { - def id: BaseModule - def name: String - def ports: Seq[Port] -} -case class DefModule(id: RawModule, name: String, ports: Seq[Port], commands: Seq[Command]) extends Component -case class DefBlackBox(id: BaseBlackBox, name: String, ports: Seq[Port], topDir: SpecifiedDirection, params: Map[String, Param]) extends Component - -case class Circuit(name: String, components: Seq[Component], annotations: Seq[ChiselAnnotation] = Seq.empty) diff --git a/chiselFrontend/src/main/scala/chisel3/package.scala b/chiselFrontend/src/main/scala/chisel3/package.scala deleted file mode 100644 index 65bfdeb7..00000000 --- a/chiselFrontend/src/main/scala/chisel3/package.scala +++ /dev/null @@ -1,229 +0,0 @@ -// See LICENSE for license details. - -import chisel3.internal.firrtl.BinaryPoint - -/** This package contains the main chisel3 API. - */ -package object chisel3 { // scalastyle:ignore package.object.name - import internal.firrtl.{Port, Width} - import internal.Builder - - import scala.language.implicitConversions - - /** - * These implicit classes allow one to convert scala.Int|scala.BigInt to - * Chisel.UInt|Chisel.SInt by calling .asUInt|.asSInt on them, respectively. - * The versions .asUInt(width)|.asSInt(width) are also available to explicitly - * mark a width for the new literal. - * - * Also provides .asBool to scala.Boolean and .asUInt to String - * - * Note that, for stylistic reasons, one should avoid extracting immediately - * after this call using apply, ie. 0.asUInt(1)(0) due to potential for - * confusion (the 1 is a bit length and the 0 is a bit extraction position). - * Prefer storing the result and then extracting from it. - * - * Implementation note: the empty parameter list (like `U()`) is necessary to prevent - * interpreting calls that have a non-Width parameter as a chained apply, otherwise things like - * `0.asUInt(16)` (instead of `16.W`) compile without error and produce undesired results. - */ - implicit class fromBigIntToLiteral(bigint: BigInt) { - /** Int to Bool conversion, allowing compact syntax like 1.B and 0.B - */ - def B: Bool = bigint match { // scalastyle:ignore method.name - case bigint if bigint == 0 => Bool.Lit(false) - case bigint if bigint == 1 => Bool.Lit(true) - case bigint => Builder.error(s"Cannot convert $bigint to Bool, must be 0 or 1"); Bool.Lit(false) - } - /** Int to UInt conversion, recommended style for constants. - */ - def U: UInt = UInt.Lit(bigint, Width()) // scalastyle:ignore method.name - /** Int to SInt conversion, recommended style for constants. - */ - def S: SInt = SInt.Lit(bigint, Width()) // scalastyle:ignore method.name - /** Int to UInt conversion with specified width, recommended style for constants. - */ - def U(width: Width): UInt = UInt.Lit(bigint, width) // scalastyle:ignore method.name - /** Int to SInt conversion with specified width, recommended style for constants. - */ - def S(width: Width): SInt = SInt.Lit(bigint, width) // scalastyle:ignore method.name - - /** Int to UInt conversion, recommended style for variables. - */ - def asUInt(): UInt = UInt.Lit(bigint, Width()) - /** Int to SInt conversion, recommended style for variables. - */ - def asSInt(): SInt = SInt.Lit(bigint, Width()) - /** Int to UInt conversion with specified width, recommended style for variables. - */ - def asUInt(width: Width): UInt = UInt.Lit(bigint, width) - /** Int to SInt conversion with specified width, recommended style for variables. - */ - def asSInt(width: Width): SInt = SInt.Lit(bigint, width) - } - - implicit class fromIntToLiteral(int: Int) extends fromBigIntToLiteral(int) - implicit class fromLongToLiteral(long: Long) extends fromBigIntToLiteral(long) - - implicit class fromStringToLiteral(str: String) { - /** String to UInt parse, recommended style for constants. - */ - def U: UInt = str.asUInt() // scalastyle:ignore method.name - /** String to UInt parse with specified width, recommended style for constants. - */ - def U(width: Width): UInt = str.asUInt(width) // scalastyle:ignore method.name - - /** String to UInt parse, recommended style for variables. - */ - def asUInt(): UInt = { - val bigInt = parse(str) - UInt.Lit(bigInt, Width(bigInt.bitLength max 1)) - } - /** String to UInt parse with specified width, recommended style for variables. - */ - def asUInt(width: Width): UInt = UInt.Lit(parse(str), width) - - protected def parse(n: String): BigInt = { - val (base, num) = n.splitAt(1) - val radix = base match { - case "x" | "h" => 16 - case "d" => 10 - case "o" => 8 - case "b" => 2 - case _ => Builder.error(s"Invalid base $base"); 2 - } - BigInt(num.filterNot(_ == '_'), radix) - } - } - - implicit class fromIntToBinaryPoint(int: Int) { - def BP: BinaryPoint = BinaryPoint(int) // scalastyle:ignore method.name - } - - implicit class fromBooleanToLiteral(boolean: Boolean) { - /** Boolean to Bool conversion, recommended style for constants. - */ - def B: Bool = Bool.Lit(boolean) // scalastyle:ignore method.name - - /** Boolean to Bool conversion, recommended style for variables. - */ - def asBool(): Bool = Bool.Lit(boolean) - } - - // Fixed Point is experimental for now, but we alias the implicit conversion classes here - // to minimize disruption with existing code. - implicit class fromDoubleToLiteral(double: Double) - extends experimental.FixedPoint.Implicits.fromDoubleToLiteral(double) - - implicit class fromBigDecimalToLiteral(bigDecimal: BigDecimal) - extends experimental.FixedPoint.Implicits.fromBigDecimalToLiteral(bigDecimal) - - // Interval is experimental for now, but we alias the implicit conversion classes here - // to minimize disruption with existing code. - implicit class fromIntToLiteralInterval(int: Int) - extends experimental.Interval.Implicits.fromIntToLiteralInterval(int) - - implicit class fromLongToLiteralInterval(long: Long) - extends experimental.Interval.Implicits.fromLongToLiteralInterval(long) - - implicit class fromBigIntToLiteralInterval(bigInt: BigInt) - extends experimental.Interval.Implicits.fromBigIntToLiteralInterval(bigInt) - - implicit class fromDoubleToLiteralInterval(double: Double) - extends experimental.Interval.Implicits.fromDoubleToLiteralInterval(double) - - implicit class fromBigDecimalToLiteralInterval(bigDecimal: BigDecimal) - extends experimental.Interval.Implicits.fromBigDecimalToLiteralInterval(bigDecimal) - - implicit class fromIntToWidth(int: Int) { - def W: Width = Width(int) // scalastyle:ignore method.name - } - - val WireInit = WireDefault - - object Vec extends VecFactory - - // Some possible regex replacements for the literal specifier deprecation: - // (note: these are not guaranteed to handle all edge cases! check all replacements!) - // Bool\((true|false)\) - // => $1.B - // UInt\(width\s*=\s*(\d+|[_a-zA-Z][_0-9a-zA-Z]*)\) - // => UInt($1.W) - // (UInt|SInt|Bits).width\((\d+|[_a-zA-Z][_0-9a-zA-Z]*)\) - // => $1($2.W) - // (U|S)Int\((-?\d+|0[xX][0-9a-fA-F]+)\) - // => $2.$1 - // UInt\((\d+|0[xX][0-9a-fA-F]+),\s*(?:width\s*=)?\s*(\d+|[_a-zA-Z][_0-9a-zA-Z]*)\) - // => $1.U($2.W) - // (UInt|SInt|Bool)\(([_a-zA-Z][_0-9a-zA-Z]*)\) - // => $2.as$1 - // (UInt|SInt)\(([_a-zA-Z][_0-9a-zA-Z]*),\s*(?:width\s*=)?\s*(\d+|[_a-zA-Z][_0-9a-zA-Z]*)\) - // => $2.as$1($3.W) - - object Bits extends UIntFactory - object UInt extends UIntFactory - object SInt extends SIntFactory - object Bool extends BoolFactory - - type InstanceId = internal.InstanceId - - type Module = chisel3.internal.LegacyModule - - /** Implicit for custom Printable string interpolator */ - implicit class PrintableHelper(val sc: StringContext) extends AnyVal { - /** Custom string interpolator for generating Printables: p"..." - * Will call .toString on any non-Printable arguments (mimicking s"...") - */ - def p(args: Any*): Printable = { - sc.checkLengths(args) // Enforce sc.parts.size == pargs.size + 1 - val pargs: Seq[Option[Printable]] = args map { - case p: Printable => Some(p) - case d: Data => Some(d.toPrintable) - case any => for { - v <- Option(any) // Handle null inputs - str = v.toString - if !str.isEmpty // Handle empty Strings - } yield PString(str) - } - val parts = sc.parts map StringContext.treatEscapes - // Zip sc.parts and pargs together ito flat Seq - // eg. Seq(sc.parts(0), pargs(0), sc.parts(1), pargs(1), ...) - val seq = for { // append None because sc.parts.size == pargs.size + 1 - (literal, arg) <- parts zip (pargs :+ None) - optPable <- Seq(Some(PString(literal)), arg) - pable <- optPable // Remove Option[_] - } yield pable - Printables(seq) - } - } - - implicit def string2Printable(str: String): Printable = PString(str) - - type ChiselException = internal.ChiselException - - // Debugger/Tester access to internal Chisel data structures and methods. - def getDataElements(a: Aggregate): Seq[Element] = { - a.allElements - } - def getModulePorts(m: Module): Seq[Port] = m.getPorts - // Invalidate API - a DontCare element for explicit assignment to outputs, - // indicating the signal is intentionally not driven. - val DontCare = chisel3.internal.InternalDontCare - - class BindingException(message: String) extends ChiselException(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) - // Connection exceptions. - case class BiConnectException(message: String) extends ChiselException(message) - case class MonoConnectException(message: String) extends ChiselException(message) -} diff --git a/core/src/main/scala/chisel3/Aggregate.scala b/core/src/main/scala/chisel3/Aggregate.scala new file mode 100644 index 00000000..6c1e8dfb --- /dev/null +++ b/core/src/main/scala/chisel3/Aggregate.scala @@ -0,0 +1,1016 @@ +// See LICENSE for license details. + +package chisel3 + +import scala.collection.immutable.ListMap +import scala.collection.mutable.{HashSet, LinkedHashMap} +import scala.language.experimental.macros + +import chisel3.experimental.BaseModule +import chisel3.experimental.BundleLiteralException +import chisel3.experimental.EnumType +import chisel3.internal._ +import chisel3.internal.Builder.pushCommand +import chisel3.internal.firrtl._ +import chisel3.internal.sourceinfo._ + +class AliasedAggregateFieldException(message: String) extends ChiselException(message) + +/** An abstract class for data types that solely consist of (are an aggregate + * of) other Data objects. + */ +sealed abstract class Aggregate extends Data { + private[chisel3] override def bind(target: Binding, parentDirection: SpecifiedDirection) { // scalastyle:ignore cyclomatic.complexity line.size.limit + binding = target + + val resolvedDirection = SpecifiedDirection.fromParent(parentDirection, specifiedDirection) + val duplicates = getElements.groupBy(identity).collect { case (x, elts) if elts.size > 1 => x } + if (!duplicates.isEmpty) { + throw new AliasedAggregateFieldException(s"Aggregate $this contains aliased fields $duplicates") + } + for (child <- getElements) { + child.bind(ChildBinding(this), resolvedDirection) + } + + // Check that children obey the directionality rules. + val childDirections = getElements.map(_.direction).toSet - ActualDirection.Empty + direction = ActualDirection.fromChildren(childDirections, resolvedDirection) match { + case Some(dir) => dir + case None => + val childWithDirections = getElements zip getElements.map(_.direction) + throw MixedDirectionAggregateException( + s"Aggregate '$this' can't have elements that are both directioned and undirectioned: $childWithDirections") + } + } + + override def litOption: Option[BigInt] = None // TODO implement me + + /** Returns a Seq of the immediate contents of this Aggregate, in order. + */ + def getElements: Seq[Data] + + private[chisel3] def width: Width = getElements.map(_.width).foldLeft(0.W)(_ + _) + private[chisel3] def legacyConnect(that: Data)(implicit sourceInfo: SourceInfo): Unit = { + // If the source is a DontCare, generate a DefInvalid for the sink, + // otherwise, issue a Connect. + if (that == DontCare) { + pushCommand(DefInvalid(sourceInfo, Node(this))) + } else { + pushCommand(BulkConnect(sourceInfo, Node(this), Node(that))) + } + } + + override def do_asUInt(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = { + SeqUtils.do_asUInt(flatten.map(_.asUInt())) + } + private[chisel3] override def connectFromBits(that: Bits)(implicit sourceInfo: SourceInfo, + compileOptions: CompileOptions): Unit = { + var i = 0 + val bits = if (that.isLit) that else WireDefault(UInt(this.width), that) // handles width padding + for (x <- flatten) { + val fieldWidth = x.getWidth + if (fieldWidth > 0) { + x.connectFromBits(bits(i + fieldWidth - 1, i)) + i += fieldWidth + } else { + // There's a zero-width field in this bundle. + // Zero-width fields can't really be assigned to, but the frontend complains if there are uninitialized fields, + // so we assign it to DontCare. We can't use connectFromBits() on DontCare, so use := instead. + x := DontCare + } + } + } +} + +trait VecFactory extends SourceInfoDoc { + /** Creates a new [[Vec]] with `n` entries of the specified data type. + * + * @note elements are NOT assigned by default and have no value + */ + def apply[T <: Data](n: Int, gen: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Vec[T] = { + if (compileOptions.declaredTypeMustBeUnbound) { + requireIsChiselType(gen, "vec type") + } + new Vec(gen.cloneTypeFull, n) + } + + /** Truncate an index to implement modulo-power-of-2 addressing. */ + private[chisel3] def truncateIndex(idx: UInt, n: BigInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = { // scalastyle:ignore line.size.limit + // scalastyle:off if.brace + val w = (n-1).bitLength + if (n <= 1) 0.U + else if (idx.width.known && idx.width.get <= w) idx + else if (idx.width.known) idx(w-1,0) + else (idx | 0.U(w.W))(w-1,0) + // scalastyle:on if.brace + } +} + +// scalastyle:off line.size.limit +/** A vector (array) of [[Data]] elements. Provides hardware versions of various + * collection transformation functions found in software array implementations. + * + * Careful consideration should be given over the use of [[Vec]] vs + * [[scala.collection.immutable.Seq Seq]] or some other Scala collection. In general [[Vec]] only + * needs to be used when there is a need to express the hardware collection in a [[Reg]] or IO + * [[Bundle]] or when access to elements of the array is indexed via a hardware signal. + * + * Example of indexing into a [[Vec]] using a hardware address and where the [[Vec]] is defined in + * an IO [[Bundle]] + * + * {{{ + * val io = IO(new Bundle { + * val in = Input(Vec(20, UInt(16.W))) + * val addr = Input(UInt(5.W)) + * val out = Output(UInt(16.W)) + * }) + * io.out := io.in(io.addr) + * }}} + * + * @tparam T type of elements + * + * @note + * - when multiple conflicting assignments are performed on a Vec element, the last one takes effect (unlike Mem, where the result is undefined) + * - Vecs, unlike classes in Scala's collection library, are propagated intact to FIRRTL as a vector type, which may make debugging easier + */ +// scalastyle:on line.size.limit +sealed class Vec[T <: Data] private[chisel3] (gen: => T, val length: Int) + extends Aggregate with VecLike[T] { + override def toString: String = { + val elementType = sample_element.cloneType + s"$elementType[$length]$bindingToString" + } + + private[chisel3] override def typeEquivalent(that: Data): Boolean = that match { + case that: Vec[T] => + this.length == that.length && + (this.sample_element typeEquivalent that.sample_element) + case _ => false + } + + private[chisel3] override def bind(target: Binding, parentDirection: SpecifiedDirection) { + binding = target + + val resolvedDirection = SpecifiedDirection.fromParent(parentDirection, specifiedDirection) + sample_element.bind(SampleElementBinding(this), resolvedDirection) + for (child <- getElements) { // assume that all children are the same + child.bind(ChildBinding(this), resolvedDirection) + } + + // Since all children are the same, we can just use the sample_element rather than all children + // .get is safe because None means mixed directions, we only pass 1 so that's not possible + direction = ActualDirection.fromChildren(Set(sample_element.direction), resolvedDirection).get + } + + // Note: the constructor takes a gen() function instead of a Seq to enforce + // that all elements must be the same and because it makes FIRRTL generation + // simpler. + private lazy val self: Seq[T] = { + val _self = Vector.fill(length)(gen) + for ((elt, i) <- _self.zipWithIndex) + elt.setRef(this, i) + _self + } + + /** + * sample_element 'tracks' all changes to the elements. + * For consistency, sample_element is always used for creating dynamically + * indexed ports and outputing the FIRRTL type. + * + * Needed specifically for the case when the Vec is length 0. + */ + 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 + // to deprecate allElements in favor of dispatched functions to Data or + // a pattern matched recursive descent + private[chisel3] final override def allElements: Seq[Element] = + (sample_element +: self).flatMap(_.allElements) + + /** Strong bulk connect, assigning elements in this Vec from elements in a Seq. + * + * @note the length of this Vec must match the length of the input Seq + */ + def <> (that: Seq[T])(implicit sourceInfo: SourceInfo, moduleCompileOptions: CompileOptions): Unit = { + if (this.length != that.length) { + Builder.error("Vec and Seq being bulk connected have different lengths!") + } + for ((a, b) <- this zip that) + a <> b + } + + // TODO: eliminate once assign(Seq) isn't ambiguous with assign(Data) since Vec extends Seq and Data + def <> (that: Vec[T])(implicit sourceInfo: SourceInfo, moduleCompileOptions: CompileOptions): Unit = this bulkConnect that.asInstanceOf[Data] // scalastyle:ignore line.size.limit + + /** Strong bulk connect, assigning elements in this Vec from elements in a Seq. + * + * @note the length of this Vec must match the length of the input Seq + */ + def := (that: Seq[T])(implicit sourceInfo: SourceInfo, moduleCompileOptions: CompileOptions): Unit = { + require(this.length == that.length, s"Cannot assign to a Vec of length ${this.length} from a Seq of different length ${that.length}") + for ((a, b) <- this zip that) + a := b + } + + // TODO: eliminate once assign(Seq) isn't ambiguous with assign(Data) since Vec extends Seq and Data + def := (that: Vec[T])(implicit sourceInfo: SourceInfo, moduleCompileOptions: CompileOptions): Unit = this connect that + + /** Creates a dynamically indexed read or write accessor into the array. + */ + override def apply(p: UInt): T = macro CompileOptionsTransform.pArg + + /** @group SourceInfoTransformMacro */ + def do_apply(p: UInt)(implicit compileOptions: CompileOptions): T = { + requireIsHardware(this, "vec") + requireIsHardware(p, "vec index") + val port = gen + + // 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 => SpecifiedDirection.Input + case ActualDirection.Output => SpecifiedDirection.Output + case ActualDirection.Bidirectional(ActualDirection.Default) | ActualDirection.Unspecified => + SpecifiedDirection.Unspecified + case ActualDirection.Bidirectional(ActualDirection.Flipped) => SpecifiedDirection.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 + } + + /** Creates a statically indexed read or write accessor into the array. + */ + def apply(idx: Int): T = self(idx) + + override def cloneType: this.type = { + new Vec(gen.cloneTypeFull, length).asInstanceOf[this.type] + } + + override def getElements: Seq[Data] = + (0 until length).map(apply(_)) + + /** Default "pretty-print" implementation + * Analogous to printing a Seq + * Results in "Vec(elt0, elt1, ...)" + */ + def toPrintable: Printable = { + // scalastyle:off if.brace + val elts = + if (length == 0) List.empty[Printable] + else self flatMap (e => List(e.toPrintable, PString(", "))) dropRight 1 + // scalastyle:on if.brace + PString("Vec(") + Printables(elts) + PString(")") + } + + /** A reduce operation in a tree like structure instead of sequentially + * @example An adder tree + * {{{ + * val sumOut = inputNums.reduceTree((a: T, b: T) => (a + b)) + * }}} + */ + def reduceTree(redOp: (T, T) => T): T = macro VecTransform.reduceTreeDefault + + /** A reduce operation in a tree like structure instead of sequentially + * @example A pipelined adder tree + * {{{ + * val sumOut = inputNums.reduceTree( + * (a: T, b: T) => RegNext(a + b), + * (a: T) => RegNext(a) + * ) + * }}} + */ + def reduceTree(redOp: (T, T) => T, layerOp: (T) => T): T = macro VecTransform.reduceTree + + def do_reduceTree(redOp: (T, T) => T, layerOp: (T) => T = (x: T) => x) + (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions) : T = { + require(!isEmpty, "Cannot apply reduction on a vec of size 0") + var curLayer = this + while (curLayer.length > 1) { + curLayer = VecInit(curLayer.grouped(2).map( x => + if (x.length == 1) layerOp(x(0)) else redOp(x(0), x(1)) + ).toSeq) + } + curLayer(0) + } +} + +object VecInit extends SourceInfoDoc { + /** Creates a new [[Vec]] composed of elements of the input Seq of [[Data]] + * nodes. + * + * @note input elements should be of the same type (this is checked at the + * FIRRTL level, but not at the Scala / Chisel level) + * @note the width of all output elements is the width of the largest input + * element + * @note output elements are connected from the input elements + */ + def apply[T <: Data](elts: Seq[T]): Vec[T] = macro VecTransform.apply_elts + + /** @group SourceInfoTransformMacro */ + def do_apply[T <: Data](elts: Seq[T])(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Vec[T] = { + // REVIEW TODO: this should be removed in favor of the apply(elts: T*) + // varargs constructor, which is more in line with the style of the Scala + // collection API. However, a deprecation phase isn't possible, since + // changing apply(elt0, elts*) to apply(elts*) causes a function collision + // with apply(Seq) after type erasure. Workarounds by either introducing a + // DummyImplicit or additional type parameter will break some code. + + // Check that types are homogeneous. Width mismatch for Elements is safe. + require(!elts.isEmpty) + elts.foreach(requireIsHardware(_, "vec element")) + + val vec = Wire(Vec(elts.length, cloneSupertype(elts, "Vec"))) + + // 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 + } + + /** Creates a new [[Vec]] composed of the input [[Data]] nodes. + * + * @note input elements should be of the same type (this is checked at the + * FIRRTL level, but not at the Scala / Chisel level) + * @note the width of all output elements is the width of the largest input + * element + * @note output elements are connected from the input elements + */ + def apply[T <: Data](elt0: T, elts: T*): Vec[T] = macro VecTransform.apply_elt0 + + /** @group SourceInfoTransformMacro */ + def do_apply[T <: Data](elt0: T, elts: T*)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Vec[T] = + apply(elt0 +: elts.toSeq) + + /** Creates a new [[Vec]] of length `n` composed of the results of the given + * function applied over a range of integer values starting from 0. + * + * @param n number of elements in the vector (the function is applied from + * 0 to `n-1`) + * @param gen function that takes in an Int (the index) and returns a + * [[Data]] that becomes the output element + */ + def tabulate[T <: Data](n: Int)(gen: (Int) => T): Vec[T] = macro VecTransform.tabulate + + /** @group SourceInfoTransformMacro */ + def do_tabulate[T <: Data](n: Int)(gen: (Int) => T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Vec[T] = // scalastyle:ignore line.size.limit + apply((0 until n).map(i => gen(i))) +} + +/** A trait for [[Vec]]s containing common hardware generators for collection + * operations. + */ +trait VecLike[T <: Data] extends collection.IndexedSeq[T] with HasId with SourceInfoDoc { + def apply(p: UInt): T = macro CompileOptionsTransform.pArg + + /** @group SourceInfoTransformMacro */ + def do_apply(p: UInt)(implicit compileOptions: CompileOptions): T + + // IndexedSeq has its own hashCode/equals that we must not use + override def hashCode: Int = super[HasId].hashCode + override def equals(that: Any): Boolean = super[HasId].equals(that) + + /** Outputs true if p outputs true for every element. + */ + def forall(p: T => Bool): Bool = macro SourceInfoTransform.pArg + + /** @group SourceInfoTransformMacro */ + def do_forall(p: T => Bool)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = + (this map p).fold(true.B)(_ && _) + + /** Outputs true if p outputs true for at least one element. + */ + def exists(p: T => Bool): Bool = macro SourceInfoTransform.pArg + + /** @group SourceInfoTransformMacro */ + def do_exists(p: T => Bool)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = + (this map p).fold(false.B)(_ || _) + + /** Outputs true if the vector contains at least one element equal to x (using + * the === operator). + */ + def contains(x: T)(implicit ev: T <:< UInt): Bool = macro VecTransform.contains + + /** @group SourceInfoTransformMacro */ + def do_contains(x: T)(implicit sourceInfo: SourceInfo, ev: T <:< UInt, compileOptions: CompileOptions): Bool = + this.exists(_ === x) + + /** Outputs the number of elements for which p is true. + */ + def count(p: T => Bool): UInt = macro SourceInfoTransform.pArg + + /** @group SourceInfoTransformMacro */ + def do_count(p: T => Bool)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = + SeqUtils.count(this map p) + + /** Helper function that appends an index (literal value) to each element, + * useful for hardware generators which output an index. + */ + private def indexWhereHelper(p: T => Bool) = this map p zip (0 until length).map(i => i.asUInt) + + /** Outputs the index of the first element for which p outputs true. + */ + def indexWhere(p: T => Bool): UInt = macro SourceInfoTransform.pArg + + /** @group SourceInfoTransformMacro */ + def do_indexWhere(p: T => Bool)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = + SeqUtils.priorityMux(indexWhereHelper(p)) + + /** Outputs the index of the last element for which p outputs true. + */ + def lastIndexWhere(p: T => Bool): UInt = macro SourceInfoTransform.pArg + + /** @group SourceInfoTransformMacro */ + def do_lastIndexWhere(p: T => Bool)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = + SeqUtils.priorityMux(indexWhereHelper(p).reverse) + + /** Outputs the index of the element for which p outputs true, assuming that + * the there is exactly one such element. + * + * The implementation may be more efficient than a priority mux, but + * incorrect results are possible if there is not exactly one true element. + * + * @note the assumption that there is only one element for which p outputs + * true is NOT checked (useful in cases where the condition doesn't always + * hold, but the results are not used in those cases) + */ + def onlyIndexWhere(p: T => Bool): UInt = macro SourceInfoTransform.pArg + + /** @group SourceInfoTransformMacro */ + def do_onlyIndexWhere(p: T => Bool)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = + SeqUtils.oneHotMux(indexWhereHelper(p)) +} + +/** Base class for Aggregates based on key values pairs of String and Data + * + * Record should only be extended by libraries and fairly sophisticated generators. + * RTL writers should use [[Bundle]]. See [[Record#elements]] for an example. + */ +abstract class Record(private[chisel3] implicit val compileOptions: CompileOptions) extends Aggregate { + private[chisel3] override def bind(target: Binding, parentDirection: SpecifiedDirection): Unit = { + try { + super.bind(target, parentDirection) + } catch { // nasty compatibility mode shim, where anything flies + case e: MixedDirectionAggregateException if !compileOptions.dontAssumeDirectionality => + val resolvedDirection = SpecifiedDirection.fromParent(parentDirection, specifiedDirection) + direction = resolvedDirection match { + case SpecifiedDirection.Unspecified => ActualDirection.Bidirectional(ActualDirection.Default) + case SpecifiedDirection.Flip => ActualDirection.Bidirectional(ActualDirection.Flipped) + case _ => ActualDirection.Bidirectional(ActualDirection.Default) + } + } + } + + /** Creates a Bundle literal of this type with specified values. this must be a chisel type. + * + * @param elems literal values, specified as a pair of the Bundle field to the literal value. + * The Bundle field is specified as a function from an object of this type to the field. + * Fields that aren't initialized to DontCare, and assignment to a wire will overwrite any + * existing value with DontCare. + * @return a Bundle literal of this type with subelement values specified + * + * @example {{{ + * class MyBundle extends Bundle { + * val a = UInt(8.W) + * val b = Bool() + * } + * + * (mew MyBundle).Lit( + * _.a -> 42.U, + * _.b -> true.B + * ) + * }}} + */ + private[chisel3] def _makeLit(elems: (this.type => (Data, Data))*): this.type = { // scalastyle:ignore line.size.limit method.length method.name cyclomatic.complexity + // Returns pairs of all fields, element-level and containers, in a Record and their path names + def getRecursiveFields(data: Data, path: String): Seq[(Data, String)] = data match { + case data: Record => data.elements.map { case (fieldName, fieldData) => + getRecursiveFields(fieldData, s"$path.$fieldName") + }.fold(Seq(data -> path)) { _ ++ _ } + case data => Seq(data -> path) // we don't support or recurse into other Aggregate types here + } + + // Returns pairs of corresponding fields between two Records of the same type + def getMatchedFields(x: Data, y: Data): Seq[(Data, Data)] = (x, y) match { + case (x: Element, y: Element) => + require(x typeEquivalent y) + Seq(x -> y) + case (x: Record, y: Record) => + (x.elements zip y.elements).map { case ((xName, xElt), (yName, yElt)) => + require(xName == yName) // assume fields returned in same, deterministic order + getMatchedFields(xElt, yElt) + }.fold(Seq(x -> y)) { _ ++ _ } + } + + requireIsChiselType(this, "bundle literal constructor model") + val clone = cloneType + val cloneFields = getRecursiveFields(clone, "(bundle root)").toMap + + // Create the Bundle literal binding from litargs of arguments + val bundleLitMap = elems.map { fn => fn(clone) }.flatMap { case (field, value) => + val fieldName = cloneFields.getOrElse(field, + throw new BundleLiteralException(s"field $field (with value $value) is not a field," + + s" ensure the field is specified as a function returning a field on an object of class ${this.getClass}," + + s" eg '_.a' to select hypothetical bundle field 'a'") + ) + val valueBinding = value.topBindingOpt match { + case Some(litBinding: LitBinding) => litBinding + case _ => throw new BundleLiteralException(s"field $fieldName specified with non-literal value $value") + } + + field match { // Get the litArg(s) for this field + case field: Bits => + if (field.getClass != value.getClass) { // TODO typeEquivalent is too strict because it checks width + throw new BundleLiteralException(s"Field $fieldName $field specified with non-type-equivalent value $value") + } + val litArg = valueBinding match { + case ElementLitBinding(litArg) => litArg + case BundleLitBinding(litMap) => litMap.getOrElse(value, + throw new BundleLiteralException(s"Field $fieldName specified with unspecified value")) + } + Seq(field -> litArg) + case field: Record => + if (!(field typeEquivalent value)) { + throw new BundleLiteralException(s"field $fieldName $field specified with non-type-equivalent value $value") + } + // Copy the source BundleLitBinding with fields (keys) remapped to the clone + val remap = getMatchedFields(value, field).toMap + value.topBinding.asInstanceOf[BundleLitBinding].litMap.map { case (valueField, valueValue) => + remap(valueField) -> valueValue + } + case field: EnumType => { + if (!(field typeEquivalent value)) { + throw new BundleLiteralException(s"field $fieldName $field specified with non-type-equivalent enum value $value") + } + val litArg = valueBinding match { + case ElementLitBinding(litArg) => litArg + } + Seq(field -> litArg) + } + case _ => throw new BundleLiteralException(s"unsupported field $fieldName of type $field") + } + } // don't convert to a Map yet to preserve duplicate keys + val duplicates = bundleLitMap.map(_._1).groupBy(identity).collect { case (x, elts) if elts.size > 1 => x } + if (!duplicates.isEmpty) { + val duplicateNames = duplicates.map(cloneFields(_)).mkString(", ") + throw new BundleLiteralException(s"duplicate fields $duplicateNames in Bundle literal constructor") + } + clone.bind(BundleLitBinding(bundleLitMap.toMap)) + clone + } + + /** The collection of [[Data]] + * + * This underlying datastructure is a ListMap because the elements must + * remain ordered for serialization/deserialization. Elements added later + * are higher order when serialized (this is similar to [[Vec]]). For example: + * {{{ + * // Assume we have some type MyRecord that creates a Record from the ListMap + * val record = MyRecord(ListMap("fizz" -> UInt(16.W), "buzz" -> UInt(16.W))) + * // "buzz" is higher order because it was added later than "fizz" + * record("fizz") := "hdead".U + * record("buzz") := "hbeef".U + * val uint = record.asUInt + * assert(uint === "hbeefdead".U) // This will pass + * }}} + */ + override def toString: String = { + val bindingString = topBindingOpt match { + case Some(BundleLitBinding(_)) => + val contents = elements.toList.reverse.map { case (name, data) => + s"$name=$data" + }.mkString(", ") + s"($contents)" + case _ => bindingToString + } + s"$className$bindingString" + } + + val elements: ListMap[String, Data] + + /** Name for Pretty Printing */ + def className: String = this.getClass.getSimpleName + + private[chisel3] override def typeEquivalent(that: Data): Boolean = that match { + case that: Record => + this.getClass == that.getClass && + this.elements.size == that.elements.size && + this.elements.forall{case (name, model) => + that.elements.contains(name) && + (that.elements(name) typeEquivalent model)} + case _ => false + } + + // NOTE: This sets up dependent references, it can be done before closing the Module + private[chisel3] override def _onModuleClose: Unit = { // scalastyle:ignore method.name + // Since Bundle names this via reflection, it is impossible for two elements to have the same + // identifier; however, Namespace sanitizes identifiers to make them legal for Firrtl/Verilog + // which can cause collisions + val _namespace = Namespace.empty + for ((name, elt) <- elements) { elt.setRef(this, _namespace.name(name, leadingDigitOk=true)) } + } + + private[chisel3] final def allElements: Seq[Element] = elements.toIndexedSeq.flatMap(_._2.allElements) + + override def getElements: Seq[Data] = elements.toIndexedSeq.map(_._2) + + // Helper because Bundle elements are reversed before printing + private[chisel3] def toPrintableHelper(elts: Seq[(String, Data)]): Printable = { + // scalastyle:off if.brace + val xs = + if (elts.isEmpty) List.empty[Printable] // special case because of dropRight below + else elts flatMap { case (name, data) => + List(PString(s"$name -> "), data.toPrintable, PString(", ")) + } dropRight 1 // Remove trailing ", " + // scalastyle:on if.brace + PString(s"$className(") + Printables(xs) + PString(")") + } + /** Default "pretty-print" implementation + * Analogous to printing a Map + * Results in "`\$className(elt0.name -> elt0.value, ...)`" + */ + def toPrintable: Printable = toPrintableHelper(elements.toList) +} + +/** + * Mix-in for Bundles that have arbitrary Seqs of Chisel types that aren't + * involved in hardware construction. + * + * Used to avoid raising an error/exception when a Seq is a public member of the + * bundle. + * This is useful if we those public Seq fields in the Bundle are unrelated to + * hardware construction. + */ +trait IgnoreSeqInBundle { + this: Bundle => + + override def ignoreSeq: Boolean = true +} + +class AutoClonetypeException(message: String) extends ChiselException(message) + +package experimental { + + class BundleLiteralException(message: String) extends ChiselException(message) + +} + +/** Base class for data types defined as a bundle of other data types. + * + * Usage: extend this class (either as an anonymous or named class) and define + * members variables of [[Data]] subtypes to be elements in the Bundle. + * + * Example of an anonymous IO bundle + * {{{ + * class MyModule extends Module { + * val io = IO(new Bundle { + * val in = Input(UInt(64.W)) + * val out = Output(SInt(128.W)) + * }) + * } + * }}} + * + * Or as a named class + * {{{ + * class Packet extends Bundle { + * val header = UInt(16.W) + * val addr = UInt(16.W) + * val data = UInt(32.W) + * } + * class MyModule extends Module { + * val io = IO(new Bundle { + * val inPacket = Input(new Packet) + * val outPacket = Output(new Packet) + * }) + * val reg = Reg(new Packet) + * reg <> io.inPacket + * io.outPacket <> reg + * } + * }}} + */ +abstract class Bundle(implicit compileOptions: CompileOptions) extends Record { + override def className: String = this.getClass.getSimpleName match { + case name if name.startsWith("$anon$") => "AnonymousBundle" // fallback for anonymous Bundle case + case "" => "AnonymousBundle" // ditto, but on other platforms + case name => name + } + /** The collection of [[Data]] + * + * Elements defined earlier in the Bundle are higher order upon + * serialization. For example: + * @example + * {{{ + * class MyBundle extends Bundle { + * val foo = UInt(16.W) + * val bar = UInt(16.W) + * } + * // Note that foo is higher order because its defined earlier in the Bundle + * val bundle = Wire(new MyBundle) + * bundle.foo := 0x1234.U + * bundle.bar := 0x5678.U + * val uint = bundle.asUInt + * assert(uint === "h12345678".U) // This will pass + * }}} + */ + final lazy val elements: ListMap[String, Data] = { + val nameMap = LinkedHashMap[String, Data]() + for (m <- getPublicFields(classOf[Bundle])) { + getBundleField(m) match { + case Some(d: Data) => + if (nameMap contains m.getName) { + require(nameMap(m.getName) eq d) + } else { + nameMap(m.getName) = d + } + case None => + if (!ignoreSeq) { + m.invoke(this) match { + case s: scala.collection.Seq[Any] if s.nonEmpty => s.head match { + // Ignore empty Seq() + case d: Data => throwException("Public Seq members cannot be used to define Bundle elements " + + s"(found public Seq member '${m.getName}'). " + + "Either use a Vec if all elements are of the same type, or MixedVec if the elements " + + "are of different types. If this Seq member is not intended to construct RTL, mix in the trait " + + "IgnoreSeqInBundle.") + case _ => // don't care about non-Data Seq + } + case _ => // not a Seq + } + } + } + } + ListMap(nameMap.toSeq sortWith { case ((an, a), (bn, b)) => (a._id > b._id) || ((a eq b) && (an > bn)) }: _*) + // scalastyle:ignore method.length + } + + /** + * Overridden by [[IgnoreSeqInBundle]] to allow arbitrary Seqs of Chisel elements. + */ + def ignoreSeq: Boolean = false + + /** Returns a field's contained user-defined Bundle element if it appears to + * be one, otherwise returns None. + */ + private def getBundleField(m: java.lang.reflect.Method): Option[Data] = m.invoke(this) match { + case d: Data => Some(d) + case Some(d: Data) => Some(d) + case _ => None + } + + // Memoize the outer instance for autoclonetype, especially where this is context-dependent + // (like the outer module or enclosing Bundles). + private var _outerInst: Option[Object] = None + + // For autoclonetype, record possible candidates for outer instance. + // _outerInst should always take precedence, since it should be propagated from the original + // object which has the most accurate context. + private val _containingModule: Option[BaseModule] = Builder.currentModule + private val _containingBundles: Seq[Bundle] = Builder.updateBundleStack(this) + + override def cloneType : this.type = { // scalastyle:ignore cyclomatic.complexity method.length + // This attempts to infer constructor and arguments to clone this Bundle subtype without + // requiring the user explicitly overriding cloneType. + import scala.language.existentials + import scala.reflect.runtime.universe._ + + val clazz = this.getClass + + def autoClonetypeError(desc: String): Nothing = { + throw new AutoClonetypeException(s"Unable to automatically infer cloneType on $clazz: $desc") + } + + def validateClone(clone: Bundle, equivDiagnostic: String): Unit = { + if (!clone.typeEquivalent(this)) { + autoClonetypeError(s"Automatically cloned $clone not type-equivalent to base $this. " + equivDiagnostic) + } + + for ((name, field) <- elements) { + if (clone.elements(name) eq field) { + autoClonetypeError(s"Automatically cloned $clone has field $name aliased with base $this." + + " In the future, this can be solved by wrapping the field in Field(...)," + + " see https://github.com/freechipsproject/chisel3/pull/909." + + " For now, ensure Chisel types used in the Bundle definition are passed through constructor arguments," + + " or wrapped in Input(...), Output(...), or Flipped(...) if appropriate.") + } + } + } + + val mirror = runtimeMirror(clazz.getClassLoader) + val classSymbolOption = try { + Some(mirror.reflect(this).symbol) + } catch { + case e: scala.reflect.internal.Symbols#CyclicReference => None // Workaround for a scala bug + } + + val enclosingClassOption = (clazz.getEnclosingClass, classSymbolOption) match { + case (null, _) => None + case (_, Some(classSymbol)) if classSymbol.isStatic => None // allows support for members of companion objects + case (outerClass, _) => Some(outerClass) + } + + // For compatibility with pre-3.1, where null is tried as an argument to the constructor. + // This stores potential error messages which may be used later. + var outerClassError: Option[String] = None + + // Check if this is an inner class, and if so, try to get the outer instance + val outerClassInstance = enclosingClassOption.map { outerClass => + def canAssignOuterClass(x: Object) = outerClass.isAssignableFrom(x.getClass) + + val outerInstance = _outerInst match { + case Some(outerInstance) => outerInstance // use _outerInst if defined + case None => // determine outer instance if not already recorded + try { + // Prefer this if it works, but doesn't work in all cases, namely anonymous inner Bundles + val outer = clazz.getDeclaredField("$outer").get(this) + _outerInst = Some(outer) + outer + } catch { + case (_: NoSuchFieldException | _: IllegalAccessException) => + // Fallback using guesses based on common patterns + val allOuterCandidates = Seq( + _containingModule.toSeq, + _containingBundles + ).flatten.distinct + allOuterCandidates.filter(canAssignOuterClass(_)) match { + case outer :: Nil => + _outerInst = Some(outer) // record the guess for future use + outer + case Nil => // TODO: replace with fatal autoClonetypeError once compatibility period is dropped + outerClassError = Some(s"Unable to determine instance of outer class $outerClass," + + s" no candidates assignable to outer class types; examined $allOuterCandidates") + null + case candidates => // TODO: replace with fatal autoClonetypeError once compatibility period is dropped + outerClassError = Some(s"Unable to determine instance of outer class $outerClass," + + s" multiple possible candidates $candidates assignable to outer class type") + null + } + } + } + (outerClass, outerInstance) + } + + // If possible (constructor with no arguments), try Java reflection first + // This handles two cases that Scala reflection doesn't: + // 1. getting the ClassSymbol of a class with an anonymous outer class fails with a + // CyclicReference exception + // 2. invoking the constructor of an anonymous inner class seems broken (it expects the outer + // class as an argument, but fails because the number of arguments passed in is incorrect) + if (clazz.getConstructors.size == 1) { + var ctor = clazz.getConstructors.head + val argTypes = ctor.getParameterTypes.toList + val clone = (argTypes, outerClassInstance) match { + case (Nil, None) => // no arguments, no outer class, invoke constructor directly + Some(ctor.newInstance().asInstanceOf[this.type]) + case (argType :: Nil, Some((_, outerInstance))) => + if (outerInstance == null) { + Builder.deprecated(s"chisel3.1 autoclonetype failed, falling back to 3.0 behavior using null as the outer instance." + // scalastyle:ignore line.size.limit + s" Autoclonetype failure reason: ${outerClassError.get}", + Some(s"$clazz")) + Some(ctor.newInstance(outerInstance).asInstanceOf[this.type]) + } else if (argType isAssignableFrom outerInstance.getClass) { + Some(ctor.newInstance(outerInstance).asInstanceOf[this.type]) + } else { + None + } + case _ => None + + } + clone match { + case Some(clone) => + clone._outerInst = this._outerInst + validateClone(clone, "Constructor argument values were not inferred, ensure constructor is deterministic.") + return clone.asInstanceOf[this.type] + case None => + } + } + + // Get constructor parameters and accessible fields + val classSymbol = classSymbolOption.getOrElse(autoClonetypeError(s"scala reflection failed." + + " This is known to occur with inner classes on anonymous outer classes." + + " In those cases, autoclonetype only works with no-argument constructors, or you can define a custom cloneType.")) // scalastyle:ignore line.size.limit + + val decls = classSymbol.typeSignature.decls + val ctors = decls.collect { case meth: MethodSymbol if meth.isConstructor => meth } + if (ctors.size != 1) { + autoClonetypeError(s"found multiple constructors ($ctors)." + + " Either remove all but the default constructor, or define a custom cloneType method.") + } + val ctor = ctors.head + val ctorParamss = ctor.paramLists + val ctorParams = ctorParamss match { + case Nil => List() + case ctorParams :: Nil => ctorParams + case ctorParams :: ctorImplicits :: Nil => ctorParams ++ ctorImplicits + case _ => autoClonetypeError(s"internal error, unexpected ctorParamss = $ctorParamss") + } + val ctorParamsNames = ctorParams.map(_.name.toString) + + // Special case for anonymous inner classes: their constructor consists of just the outer class reference + // Scala reflection on anonymous inner class constructors seems broken + if (ctorParams.size == 1 && outerClassInstance.isDefined && + ctorParams.head.typeSignature == mirror.classSymbol(outerClassInstance.get._1).toType) { + // Fall back onto Java reflection + val ctors = clazz.getConstructors + require(ctors.size == 1) // should be consistent with Scala constructors + try { + val clone = ctors.head.newInstance(outerClassInstance.get._2).asInstanceOf[this.type] + clone._outerInst = this._outerInst + + validateClone(clone, "Outer class instance was inferred, ensure constructor is deterministic.") + return clone + } catch { + case e @ (_: java.lang.reflect.InvocationTargetException | _: IllegalArgumentException) => + autoClonetypeError(s"unexpected failure at constructor invocation, got $e.") + } + } + + // Get all the class symbols up to (but not including) Bundle and get all the accessors. + // (each ClassSymbol's decls only includes those declared in the class itself) + val bundleClassSymbol = mirror.classSymbol(classOf[Bundle]) + val superClassSymbols = classSymbol.baseClasses.takeWhile(_ != bundleClassSymbol) + val superClassDecls = superClassSymbols.map(_.typeSignature.decls).flatten + val accessors = superClassDecls.collect { case meth: MethodSymbol if meth.isParamAccessor => meth } + + // Get constructor argument values + // Check that all ctor params are immutable and accessible. Immutability is required to avoid + // potential subtle bugs (like values changing after cloning). + // This also generates better error messages (all missing elements shown at once) instead of + // failing at the use site one at a time. + val accessorsName = accessors.filter(_.isStable).map(_.name.toString) + val paramsDiff = ctorParamsNames.toSet -- accessorsName.toSet + if (!paramsDiff.isEmpty) { + // scalastyle:off line.size.limit + autoClonetypeError(s"constructor has parameters (${paramsDiff.toList.sorted.mkString(", ")}) that are not both immutable and accessible." + + " Either make all parameters immutable and accessible (vals) so cloneType can be inferred, or define a custom cloneType method.") + // scalastyle:on line.size.limit + } + + // Get all the argument values + val accessorsMap = accessors.map(accessor => accessor.name.toString -> accessor).toMap + val instanceReflect = mirror.reflect(this) + val ctorParamsNameVals = ctorParamsNames.map { + paramName => paramName -> instanceReflect.reflectMethod(accessorsMap(paramName)).apply() + } + + // Opportunistic sanity check: ensure any arguments of type Data is not bound + // (which could lead to data conflicts, since it's likely the user didn't know to re-bind them). + // This is not guaranteed to catch all cases (for example, Data in Tuples or Iterables). + val boundDataParamNames = ctorParamsNameVals.collect { + case (paramName, paramVal: Data) if paramVal.topBindingOpt.isDefined => paramName + } + if (boundDataParamNames.nonEmpty) { + // scalastyle:off line.size.limit + autoClonetypeError(s"constructor parameters (${boundDataParamNames.sorted.mkString(", ")}) have values that are hardware types, which is likely to cause subtle errors." + + " Use chisel types instead: use the value before it is turned to a hardware type (with Wire(...), Reg(...), etc) or use chiselTypeOf(...) to extract the chisel type.") + // scalastyle:on line.size.limit + } + + // Clone unbound parameters in case they are being used as bundle fields. + val ctorParamsVals = ctorParamsNameVals.map { + case (_, paramVal: Data) => paramVal.cloneTypeFull + case (_, paramVal) => paramVal + } + + // Invoke ctor + val classMirror = outerClassInstance match { + case Some((_, null)) => autoClonetypeError(outerClassError.get) // deals with the null hack for 3.0 compatibility + case Some((_, outerInstance)) => mirror.reflect(outerInstance).reflectClass(classSymbol) + case _ => mirror.reflectClass(classSymbol) + } + val clone = classMirror.reflectConstructor(ctor).apply(ctorParamsVals:_*).asInstanceOf[this.type] + clone._outerInst = this._outerInst + + validateClone(clone, + "Constructor argument values were inferred:" + + " ensure that variable names are consistent and have the same value throughout the constructor chain," + + " and that the constructor is deterministic." + ) + clone + } + + /** Default "pretty-print" implementation + * Analogous to printing a Map + * Results in "`Bundle(elt0.name -> elt0.value, ...)`" + * @note The order is reversed from the order of elements in order to print + * the fields in the order they were defined + */ + override def toPrintable: Printable = toPrintableHelper(elements.toList.reverse) + // scalastyle:off method.length +} +// scalastyle:off file.size.limit diff --git a/core/src/main/scala/chisel3/Annotation.scala b/core/src/main/scala/chisel3/Annotation.scala new file mode 100644 index 00000000..e54b1bf9 --- /dev/null +++ b/core/src/main/scala/chisel3/Annotation.scala @@ -0,0 +1,94 @@ +// See LICENSE for license details. + +package chisel3.experimental + +import scala.language.existentials +import chisel3.internal.{Builder, InstanceId, LegacyModule} +import chisel3.{CompileOptions, Data} +import firrtl.Transform +import firrtl.annotations._ +import firrtl.options.Unserializable +import firrtl.transforms.{DontTouchAnnotation, NoDedupAnnotation} + +/** Interface for Annotations in Chisel + * + * Defines a conversion to a corresponding FIRRTL Annotation + */ +trait ChiselAnnotation { + /** Conversion to FIRRTL Annotation */ + def toFirrtl: Annotation +} + +/** Mixin for [[ChiselAnnotation]] that instantiates an associated FIRRTL Transform when this Annotation is present + * during a run of + * [[Driver$.execute(args:Array[String],dut:()=>chisel3\.RawModule)* Driver.execute]]. + * Automatic Transform instantiation is *not* supported when the Circuit and Annotations are serialized before invoking + * FIRRTL. + */ +trait RunFirrtlTransform extends ChiselAnnotation { + def transformClass: Class[_ <: Transform] +} + + +// This exists for implementation reasons, we don't want people using this type directly +final case class ChiselLegacyAnnotation private[chisel3] ( + component: InstanceId, + transformClass: Class[_ <: Transform], + value: String) extends ChiselAnnotation with RunFirrtlTransform { + def toFirrtl: Annotation = Annotation(component.toNamed, transformClass, value) +} +private[chisel3] object ChiselLegacyAnnotation + +object annotate { // scalastyle:ignore object.name + def apply(anno: ChiselAnnotation): Unit = { + Builder.annotations += anno + } +} + +/** Marks that a module to be ignored in Dedup Transform in Firrtl pass + * + * @example {{{ + * def fullAdder(a: UInt, b: UInt, myName: String): UInt = { + * val m = Module(new Module { + * val io = IO(new Bundle { + * val a = Input(UInt(32.W)) + * val b = Input(UInt(32.W)) + * val out = Output(UInt(32.W)) + * }) + * override def desiredName = "adder_" + myNname + * io.out := io.a + io.b + * }) + * doNotDedup(m) + * m.io.a := a + * m.io.b := b + * m.io.out + * } + * + *class AdderTester extends Module + * with ConstantPropagationTest { + * val io = IO(new Bundle { + * val a = Input(UInt(32.W)) + * val b = Input(UInt(32.W)) + * val out = Output(Vec(2, UInt(32.W))) + * }) + * + * io.out(0) := fullAdder(io.a, io.b, "mod1") + * io.out(1) := fullAdder(io.a, io.b, "mod2") + * } + * }}} + * + * @note Calling this on [[Data]] creates an annotation that Chisel emits to a separate annotations + * file. This file must be passed to FIRRTL independently of the `.fir` file. The execute methods + * in [[chisel3.Driver]] will pass the annotations to FIRRTL automatically. + */ + +object doNotDedup { // scalastyle:ignore object.name + /** Marks a module to be ignored in Dedup Transform in Firrtl + * + * @param module The module to be marked + * @return Unmodified signal `module` + */ + def apply[T <: LegacyModule](module: T)(implicit compileOptions: CompileOptions): Unit = { + annotate(new ChiselAnnotation { def toFirrtl = NoDedupAnnotation(module.toNamed) }) + } +} diff --git a/core/src/main/scala/chisel3/Assert.scala b/core/src/main/scala/chisel3/Assert.scala new file mode 100644 index 00000000..af58bce6 --- /dev/null +++ b/core/src/main/scala/chisel3/Assert.scala @@ -0,0 +1,92 @@ +// See LICENSE for license details. + +package chisel3 + +import scala.reflect.macros.blackbox.Context +import scala.language.experimental.macros + +import chisel3.internal._ +import chisel3.internal.Builder.pushCommand +import chisel3.internal.firrtl._ +import chisel3.internal.sourceinfo.SourceInfo + +object assert { // scalastyle:ignore object.name + /** Checks for a condition to be valid in the circuit at all times. If the + * condition evaluates to false, the circuit simulation stops with an error. + * + * Does not fire when in reset (defined as the encapsulating Module's + * reset). If your definition of reset is not the encapsulating Module's + * reset, you will need to gate this externally. + * + * May be called outside of a Module (like defined in a function), so + * functions using assert make the standard Module assumptions (single clock + * and single reset). + * + * @param cond condition, assertion fires (simulation fails) when false + * @param message optional format string to print when the assertion fires + * @param data optional bits to print in the message formatting + * + * @note See [[printf.apply(fmt:String* printf]] for format string documentation + * @note currently cannot be used in core Chisel / libraries because macro + * defs need to be compiled first and the SBT project is not set up to do + * that + */ + // Macros currently can't take default arguments, so we need two functions to emulate defaults. + def apply(cond: Bool, message: String, data: Bits*)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Unit = macro apply_impl_msg_data // scalastyle:ignore line.size.limit + def apply(cond: Bool)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Unit = macro apply_impl + + def apply_impl_msg_data(c: Context)(cond: c.Tree, message: c.Tree, data: c.Tree*)(sourceInfo: c.Tree, compileOptions: c.Tree): c.Tree = { // scalastyle:ignore line.size.limit + import c.universe._ + val p = c.enclosingPosition + val condStr = s"${p.source.file.name}:${p.line} ${p.lineContent.trim}" + val apply_impl_do = symbolOf[this.type].asClass.module.info.member(TermName("apply_impl_do")) + q"$apply_impl_do($cond, $condStr, _root_.scala.Some($message), ..$data)($sourceInfo, $compileOptions)" + } + + def apply_impl(c: Context)(cond: c.Tree)(sourceInfo: c.Tree, compileOptions: c.Tree): c.Tree = { + import c.universe._ + val p = c.enclosingPosition + val condStr = s"${p.source.file.name}:${p.line} ${p.lineContent.trim}" + val apply_impl_do = symbolOf[this.type].asClass.module.info.member(TermName("apply_impl_do")) + q"$apply_impl_do($cond, $condStr, _root_.scala.None)($sourceInfo, $compileOptions)" + } + + def apply_impl_do(cond: Bool, line: String, message: Option[String], data: Bits*)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions) { // scalastyle:ignore line.size.limit + val escLine = line.replaceAll("%", "%%") + when (!(cond || Module.reset.asBool)) { + val fmt = message match { + case Some(msg) => + s"Assertion failed: $msg\n at $escLine\n" + case None => s"Assertion failed\n at $escLine\n" + } + printf.printfWithoutReset(fmt, data:_*) + pushCommand(Stop(sourceInfo, Builder.forcedClock.ref, 1)) + } + } + + /** An elaboration-time assertion, otherwise the same as the above run-time + * assertion. */ + def apply(cond: Boolean, message: => String) { + Predef.assert(cond, message) + } + + /** A workaround for default-value overloading problems in Scala, just + * 'assert(cond, "")' */ + def apply(cond: Boolean) { + Predef.assert(cond, "") + } +} + +object stop { // scalastyle:ignore object.name + /** Terminate execution with a failure code. */ + def apply(code: Int)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Unit = { + when (!Module.reset.asBool) { + pushCommand(Stop(sourceInfo, Builder.forcedClock.ref, code)) + } + } + + /** Terminate execution, indicating success. */ + def apply()(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Unit = { + stop(0) + } +} diff --git a/core/src/main/scala/chisel3/Attach.scala b/core/src/main/scala/chisel3/Attach.scala new file mode 100644 index 00000000..25c83d9a --- /dev/null +++ b/core/src/main/scala/chisel3/Attach.scala @@ -0,0 +1,46 @@ +// See LICENSE for license details. + +package chisel3.experimental + +import chisel3.RawModule +import chisel3.internal._ +import chisel3.internal.Builder.pushCommand +import chisel3.internal.firrtl._ +import chisel3.internal.sourceinfo.SourceInfo + +object attach { // scalastyle:ignore object.name + // Exceptions that can be generated by attach + case class AttachException(message: String) extends ChiselException(message) + def ConditionalAttachException: AttachException = // scalastyle:ignore method.name + AttachException(": Conditional attach is not allowed!") + + // Actual implementation + private[chisel3] def impl(elts: Seq[Analog], contextModule: RawModule)(implicit sourceInfo: SourceInfo): Unit = { + if (Builder.whenDepth != 0) throw ConditionalAttachException + + // TODO Check that references are valid and can be attached + + pushCommand(Attach(sourceInfo, elts.map(_.lref))) + } + + /** Create an electrical connection between [[Analog]] components + * + * @param elts The components to attach + * + * @example + * {{{ + * val a1 = Wire(Analog(32.W)) + * val a2 = Wire(Analog(32.W)) + * attach(a1, a2) + * }}} + */ + def apply(elts: Analog*)(implicit sourceInfo: SourceInfo): Unit = { + try { + impl(elts, Builder.forcedUserModule) + } catch { + case AttachException(message) => + throwException(elts.mkString("Attaching (", ", ", s") failed @$message")) + } + } +} + diff --git a/core/src/main/scala/chisel3/Bits.scala b/core/src/main/scala/chisel3/Bits.scala new file mode 100644 index 00000000..43c34d9d --- /dev/null +++ b/core/src/main/scala/chisel3/Bits.scala @@ -0,0 +1,2289 @@ +// See LICENSE for license details. + +package chisel3 + +import scala.language.experimental.macros + +import chisel3.experimental.{FixedPoint, Interval} +import chisel3.internal._ +import chisel3.internal.Builder.pushOp +import chisel3.internal.firrtl._ +import chisel3.internal.sourceinfo.{SourceInfo, SourceInfoTransform, SourceInfoWhiteboxTransform, + UIntTransform} +import chisel3.internal.firrtl.PrimOp._ +import _root_.firrtl.{ir => firrtlir} +import _root_.firrtl.{constraint => firrtlconstraint} + +// scalastyle:off method.name line.size.limit file.size.limit + +/** Exists to unify common interfaces of [[Bits]] and [[Reset]]. + * + * @note This is a workaround because macros cannot override abstract methods. + */ +private[chisel3] sealed trait ToBoolable extends Element { + + /** Casts this $coll to a [[Bool]] + * + * @note The width must be known and equal to 1 + */ + final def asBool(): Bool = macro SourceInfoWhiteboxTransform.noArg + + /** @group SourceInfoTransformMacro */ + def do_asBool(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool + + /** Casts this $coll to a [[Bool]] + * + * @note The width must be known and equal to 1 + */ + final def toBool(): Bool = macro SourceInfoWhiteboxTransform.noArg + + /** @group SourceInfoTransformMacro */ + def do_toBool(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool +} + +/** A data type for values represented by a single bitvector. This provides basic bitwise operations. + * + * @groupdesc Bitwise Bitwise hardware operators + * @define coll [[Bits]] + * @define sumWidthInt @note The width of the returned $coll is `width of this` + `that`. + * @define sumWidth @note The width of the returned $coll is `width of this` + `width of that`. + * @define unchangedWidth @note The width of the returned $coll is unchanged, i.e., the `width of this`. + */ +sealed abstract class Bits(private[chisel3] val width: Width) extends Element with ToBoolable { //scalastyle:off number.of.methods + // TODO: perhaps make this concrete? + // Arguments for: self-checking code (can't do arithmetic on bits) + // Arguments against: generates down to a FIRRTL UInt anyways + + // Only used for in a few cases, hopefully to be removed + private[chisel3] def cloneTypeWidth(width: Width): this.type + + def cloneType: this.type = cloneTypeWidth(width) + + /** Tail operator + * + * @param n the number of bits to remove + * @return This $coll with the `n` most significant bits removed. + * @group Bitwise + */ + final def tail(n: Int): UInt = macro SourceInfoTransform.nArg + + /** Head operator + * + * @param n the number of bits to take + * @return The `n` most significant bits of this $coll + * @group Bitwise + */ + final def head(n: Int): UInt = macro SourceInfoTransform.nArg + + /** @group SourceInfoTransformMacro */ + def do_tail(n: Int)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = { + val w = width match { + case KnownWidth(x) => + require(x >= n, s"Can't tail($n) for width $x < $n") + Width(x - n) + case UnknownWidth() => Width() + } + binop(sourceInfo, UInt(width = w), TailOp, n) + } + + /** @group SourceInfoTransformMacro */ + def do_head(n: Int)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = { + width match { + case KnownWidth(x) => require(x >= n, s"Can't head($n) for width $x < $n") + case UnknownWidth() => + } + binop(sourceInfo, UInt(Width(n)), HeadOp, n) + } + + /** Returns the specified bit on this $coll as a [[Bool]], statically addressed. + * + * @param x an index + * @return the specified bit + */ + final def apply(x: BigInt): Bool = macro SourceInfoTransform.xArg + + /** @group SourceInfoTransformMacro */ + final def do_apply(x: BigInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = { + if (x < 0) { + Builder.error(s"Negative bit indices are illegal (got $x)") + } + // This preserves old behavior while a more more consistent API is under debate + // See https://github.com/freechipsproject/chisel3/issues/867 + litOption.map { value => + (((value >> castToInt(x, "Index")) & 1) == 1).asBool + }.getOrElse { + requireIsHardware(this, "bits to be indexed") + pushOp(DefPrim(sourceInfo, Bool(), BitsExtractOp, this.ref, ILit(x), ILit(x))) + } + } + + /** Returns the specified bit on this $coll as a [[Bool]], statically addressed. + * + * @param x an index + * @return the specified bit + * @note convenience method allowing direct use of [[scala.Int]] without implicits + */ + final def apply(x: Int): Bool = macro SourceInfoTransform.xArg + + /** @group SourceInfoTransformMacro */ + final def do_apply(x: Int)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = apply(BigInt(x)) + + /** Returns the specified bit on this wire as a [[Bool]], dynamically addressed. + * + * @param x a hardware component whose value will be used for dynamic addressing + * @return the specified bit + */ + final def apply(x: UInt): Bool = macro SourceInfoTransform.xArg + + /** @group SourceInfoTransformMacro */ + final def do_apply(x: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = { + val theBits = this >> x + theBits(0) + } + + /** Returns a subset of bits on this $coll from `hi` to `lo` (inclusive), statically addressed. + * + * @example + * {{{ + * myBits = 0x5 = 0b101 + * myBits(1,0) => 0b01 // extracts the two least significant bits + * }}} + * @param x the high bit + * @param y the low bit + * @return a hardware component contain the requested bits + */ + final def apply(x: Int, y: Int): UInt = macro SourceInfoTransform.xyArg + + /** @group SourceInfoTransformMacro */ + final def do_apply(x: Int, y: Int)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = { + if (x < y || y < 0) { + Builder.error(s"Invalid bit range ($x,$y)") + } + val w = x - y + 1 + // This preserves old behavior while a more more consistent API is under debate + // See https://github.com/freechipsproject/chisel3/issues/867 + litOption.map { value => + ((value >> y) & ((BigInt(1) << w) - 1)).asUInt(w.W) + }.getOrElse { + requireIsHardware(this, "bits to be sliced") + pushOp(DefPrim(sourceInfo, UInt(Width(w)), BitsExtractOp, this.ref, ILit(x), ILit(y))) + } + } + + // REVIEW TODO: again, is this necessary? Or just have this and use implicits? + /** Returns a subset of bits on this $coll from `hi` to `lo` (inclusive), statically addressed. + * + * @example + * {{{ + * myBits = 0x5 = 0b101 + * myBits(1,0) => 0b01 // extracts the two least significant bits + * }}} + * @param x the high bit + * @param y the low bit + * @return a hardware component contain the requested bits + */ + final def apply(x: BigInt, y: BigInt): UInt = macro SourceInfoTransform.xyArg + + /** @group SourceInfoTransformMacro */ + final def do_apply(x: BigInt, y: BigInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = + apply(castToInt(x, "High index"), castToInt(y, "Low index")) + + private[chisel3] def unop[T <: Data](sourceInfo: SourceInfo, dest: T, op: PrimOp): T = { + requireIsHardware(this, "bits operated on") + pushOp(DefPrim(sourceInfo, dest, op, this.ref)) + } + private[chisel3] def binop[T <: Data](sourceInfo: SourceInfo, dest: T, op: PrimOp, other: BigInt): T = { + requireIsHardware(this, "bits operated on") + pushOp(DefPrim(sourceInfo, dest, op, this.ref, ILit(other))) + } + private[chisel3] def binop[T <: Data](sourceInfo: SourceInfo, dest: T, op: PrimOp, other: Bits): T = { + requireIsHardware(this, "bits operated on") + requireIsHardware(other, "bits operated on") + pushOp(DefPrim(sourceInfo, dest, op, this.ref, other.ref)) + } + private[chisel3] def compop(sourceInfo: SourceInfo, op: PrimOp, other: Bits): Bool = { + requireIsHardware(this, "bits operated on") + requireIsHardware(other, "bits operated on") + pushOp(DefPrim(sourceInfo, Bool(), op, this.ref, other.ref)) + } + private[chisel3] def redop(sourceInfo: SourceInfo, op: PrimOp): Bool = { + requireIsHardware(this, "bits operated on") + pushOp(DefPrim(sourceInfo, Bool(), op, this.ref)) + } + + /** Pad operator + * + * @param that the width to pad to + * @return this @coll zero padded up to width `that`. If `that` is less than the width of the original component, + * this method returns the original component. + * @note For [[SInt]]s only, this will do sign extension. + * @group Bitwise + */ + final def pad(that: Int): this.type = macro SourceInfoTransform.thatArg + + /** @group SourceInfoTransformMacro */ + def do_pad(that: Int)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): this.type = this.width match { + case KnownWidth(w) if w >= that => this + case _ => binop(sourceInfo, cloneTypeWidth(this.width max Width(that)), PadOp, that) + } + + /** Bitwise inversion operator + * + * @return this $coll with each bit inverted + * @group Bitwise + */ + final def unary_~ (): Bits = macro SourceInfoWhiteboxTransform.noArg + + /** @group SourceInfoTransformMacro */ + def do_unary_~ (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bits + + /** Static left shift operator + * + * @param that an amount to shift by + * @return this $coll with `that` many zeros concatenated to its least significant end + * $sumWidthInt + * @group Bitwise + */ + // REVIEW TODO: redundant + // REVIEW TODO: should these return this.type or Bits? + final def << (that: BigInt): Bits = macro SourceInfoWhiteboxTransform.thatArg + + /** @group SourceInfoTransformMacro */ + def do_<< (that: BigInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bits + + /** Static left shift operator + * + * @param that an amount to shift by + * @return this $coll with `that` many zeros concatenated to its least significant end + * $sumWidthInt + * @group Bitwise + */ + final def << (that: Int): Bits = macro SourceInfoWhiteboxTransform.thatArg + + /** @group SourceInfoTransformMacro */ + def do_<< (that: Int)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bits + + /** Dynamic left shift operator + * + * @param that a hardware component + * @return this $coll dynamically shifted left by `that` many places, shifting in zeros from the right + * @note The width of the returned $coll is `width of this + pow(2, width of that) - 1`. + * @group Bitwise + */ + final def << (that: UInt): Bits = macro SourceInfoWhiteboxTransform.thatArg + + /** @group SourceInfoTransformMacro */ + def do_<< (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bits + + /** Static right shift operator + * + * @param that an amount to shift by + * @return this $coll with `that` many least significant bits truncated + * $unchangedWidth + * @group Bitwise + */ + // REVIEW TODO: redundant + final def >> (that: BigInt): Bits = macro SourceInfoWhiteboxTransform.thatArg + + /** @group SourceInfoTransformMacro */ + def do_>> (that: BigInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bits + + /** Static right shift operator + * + * @param that an amount to shift by + * @return this $coll with `that` many least significant bits truncated + * $unchangedWidth + * @group Bitwise + */ + final def >> (that: Int): Bits = macro SourceInfoWhiteboxTransform.thatArg + + /** @group SourceInfoTransformMacro */ + def do_>> (that: Int)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bits + + /** Dynamic right shift operator + * + * @param that a hardware component + * @return this $coll dynamically shifted right by the value of `that` component, inserting zeros into the most + * significant bits. + * $unchangedWidth + * @group Bitwise + */ + final def >> (that: UInt): Bits = macro SourceInfoWhiteboxTransform.thatArg + + /** @group SourceInfoTransformMacro */ + def do_>> (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bits + + /** Returns the contents of this wire as a [[scala.collection.Seq]] of [[Bool]]. */ + final def toBools(): Seq[Bool] = macro SourceInfoTransform.noArg + + /** @group SourceInfoTransformMacro */ + @chiselRuntimeDeprecated + @deprecated("Use asBools instead", "3.2") + def do_toBools(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Seq[Bool] = do_asBools + + /** Returns the contents of this wire as a [[scala.collection.Seq]] of [[Bool]]. */ + final def asBools(): Seq[Bool] = macro SourceInfoTransform.noArg + + /** @group SourceInfoTransformMacro */ + def do_asBools(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Seq[Bool] = + Seq.tabulate(this.getWidth)(i => this(i)) + + /** Reinterpret this $coll as an [[SInt]] + * + * @note The arithmetic value is not preserved if the most-significant bit is set. For example, a [[UInt]] of + * width 3 and value 7 (0b111) would become an [[SInt]] of width 3 and value -1. + */ + final def asSInt(): SInt = macro SourceInfoTransform.noArg + + /** @group SourceInfoTransformMacro */ + def do_asSInt(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt + + /** Reinterpret this $coll as a [[FixedPoint]]. + * + * @note The value is not guaranteed to be preserved. For example, a [[UInt]] of width 3 and value 7 (0b111) would + * become a [[FixedPoint]] with value -1. The interpretation of the number is also affected by the specified binary + * point. '''Caution is advised!''' + */ + final def asFixedPoint(that: BinaryPoint): FixedPoint = macro SourceInfoTransform.thatArg + + /** @group SourceInfoTransformMacro */ + def do_asFixedPoint(that: BinaryPoint)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = { + throwException(s"Cannot call .asFixedPoint on $this") + } + + /** Reinterpret cast as a Interval. + * + * @note value not guaranteed to be preserved: for example, an UInt of width + * 3 and value 7 (0b111) would become a FixedInt with value -1, the interpretation + * of the number is also affected by the specified binary point. Caution advised + */ + final def asInterval(that: IntervalRange): Interval = macro SourceInfoTransform.thatArg + + def do_asInterval(that: IntervalRange)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { + throwException(s"Cannot call .asInterval on $this") + } + + final def do_asBool(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = { + width match { + case KnownWidth(1) => this(0) + case _ => throwException(s"can't covert ${this.getClass.getSimpleName}$width to Bool") + } + } + + @chiselRuntimeDeprecated + @deprecated("Use asBool instead", "3.2") + final def do_toBool(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = do_asBool + + /** Concatenation operator + * + * @param that a hardware component + * @return this $coll concatenated to the most significant end of `that` + * $sumWidth + * @group Bitwise + */ + final def ## (that: Bits): UInt = macro SourceInfoTransform.thatArg + + /** @group SourceInfoTransformMacro */ + def do_## (that: Bits)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = { + val w = this.width + that.width + pushOp(DefPrim(sourceInfo, UInt(w), ConcatOp, this.ref, that.ref)) + } + + /** Default print as [[Decimal]] */ + final def toPrintable: Printable = Decimal(this) + + protected final def validateShiftAmount(x: Int): Int = { + if (x < 0) + Builder.error(s"Negative shift amounts are illegal (got $x)") + x + } +} + +/** A data type for unsigned integers, represented as a binary bitvector. Defines arithmetic operations between other + * integer types. + * + * @define coll [[UInt]] + * @define numType $coll + * @define expandingWidth @note The width of the returned $coll is `width of this` + `1`. + * @define constantWidth @note The width of the returned $coll is unchanged, i.e., `width of this`. + */ +sealed class UInt private[chisel3] (width: Width) extends Bits(width) with Num[UInt] { + override def toString: String = { + val bindingString = litOption match { + case Some(value) => s"($value)" + case _ => bindingToString + } + s"UInt$width$bindingString" + } + + private[chisel3] override def typeEquivalent(that: Data): Boolean = + that.isInstanceOf[UInt] && this.width == that.width + + private[chisel3] override def cloneTypeWidth(w: Width): this.type = + new UInt(w).asInstanceOf[this.type] + + // TODO: refactor to share documentation with Num or add independent scaladoc + /** Unary negation (expanding width) + * + * @return a $coll equal to zero minus this $coll + * $constantWidth + * @group Arithmetic + */ + final def unary_- (): UInt = macro SourceInfoTransform.noArg + + /** Unary negation (constant width) + * + * @return a $coll equal to zero minus this $coll shifted right by one. + * $constantWidth + * @group Arithmetic + */ + final def unary_-% (): UInt = macro SourceInfoTransform.noArg + + /** @group SourceInfoTransformMacro */ + def do_unary_- (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions) : UInt = 0.U - this + /** @group SourceInfoTransformMacro */ + def do_unary_-% (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = 0.U -% this + + override def do_+ (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = this +% that + override def do_- (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = this -% that + override def do_/ (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = + binop(sourceInfo, UInt(this.width), DivideOp, that) + override def do_% (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = + binop(sourceInfo, UInt(this.width), RemOp, that) + override def do_* (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = + binop(sourceInfo, UInt(this.width + that.width), TimesOp, that) + + /** Multiplication operator + * + * @param that a hardware [[SInt]] + * @return the product of this $coll and `that` + * $sumWidth + * $singleCycleMul + * @group Arithmetic + */ + final def * (that: SInt): SInt = macro SourceInfoTransform.thatArg + /** @group SourceInfoTransformMacro */ + def do_* (that: SInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = that * this + + /** Addition operator (expanding width) + * + * @param that a hardware $coll + * @return the sum of this $coll and `that` + * $maxWidthPlusOne + * @group Arithmetic + */ + final def +& (that: UInt): UInt = macro SourceInfoTransform.thatArg + + /** Addition operator (constant width) + * + * @param that a hardware $coll + * @return the sum of this $coll and `that` + * $maxWidth + * @group Arithmetic + */ + final def +% (that: UInt): UInt = macro SourceInfoTransform.thatArg + + /** Subtraction operator (increasing width) + * + * @param that a hardware $coll + * @return the difference of this $coll less `that` + * $maxWidthPlusOne + * @group Arithmetic + */ + final def -& (that: UInt): UInt = macro SourceInfoTransform.thatArg + + /** Subtraction operator (constant width) + * + * @param that a hardware $coll + * @return the difference of this $coll less `that` + * $maxWidth + * @group Arithmetic + */ + final def -% (that: UInt): UInt = macro SourceInfoTransform.thatArg + + /** @group SourceInfoTransformMacro */ + def do_+& (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = + binop(sourceInfo, UInt((this.width max that.width) + 1), AddOp, that) + /** @group SourceInfoTransformMacro */ + def do_+% (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = + (this +& that).tail(1) + /** @group SourceInfoTransformMacro */ + def do_-& (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = + (this subtractAsSInt that).asUInt + /** @group SourceInfoTransformMacro */ + def do_-% (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = + (this subtractAsSInt that).tail(1) + + /** Bitwise and operator + * + * @param that a hardware $coll + * @return the bitwise and of this $coll and `that` + * $maxWidth + * @group Bitwise + */ + final def & (that: UInt): UInt = macro SourceInfoTransform.thatArg + + /** Bitwise or operator + * + * @param that a hardware $coll + * @return the bitwise or of this $coll and `that` + * $maxWidth + * @group Bitwise + */ + final def | (that: UInt): UInt = macro SourceInfoTransform.thatArg + + /** Bitwise exclusive or (xor) operator + * + * @param that a hardware $coll + * @return the bitwise xor of this $coll and `that` + * $maxWidth + * @group Bitwise + */ + final def ^ (that: UInt): UInt = macro SourceInfoTransform.thatArg + + // override def abs: UInt = macro SourceInfoTransform.noArg + def do_abs(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = this + + /** @group SourceInfoTransformMacro */ + def do_& (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = + binop(sourceInfo, UInt(this.width max that.width), BitAndOp, that) + /** @group SourceInfoTransformMacro */ + def do_| (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = + binop(sourceInfo, UInt(this.width max that.width), BitOrOp, that) + /** @group SourceInfoTransformMacro */ + def do_^ (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = + binop(sourceInfo, UInt(this.width max that.width), BitXorOp, that) + + /** @group SourceInfoTransformMacro */ + def do_unary_~ (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = + unop(sourceInfo, UInt(width = width), BitNotOp) + + // REVIEW TODO: Can these be defined on Bits? + /** Or reduction operator + * + * @return a hardware [[Bool]] resulting from every bit of this $coll or'd together + * @group Bitwise + */ + final def orR(): Bool = macro SourceInfoTransform.noArg + + /** And reduction operator + * + * @return a hardware [[Bool]] resulting from every bit of this $coll and'd together + * @group Bitwise + */ + final def andR(): Bool = macro SourceInfoTransform.noArg + + /** Exclusive or (xor) reduction operator + * + * @return a hardware [[Bool]] resulting from every bit of this $coll xor'd together + * @group Bitwise + */ + final def xorR(): Bool = macro SourceInfoTransform.noArg + + /** @group SourceInfoTransformMacro */ + def do_orR(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = redop(sourceInfo, OrReduceOp) + /** @group SourceInfoTransformMacro */ + def do_andR(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = redop(sourceInfo, AndReduceOp) + /** @group SourceInfoTransformMacro */ + def do_xorR(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = redop(sourceInfo, XorReduceOp) + + override def do_< (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, LessOp, that) + override def do_> (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, GreaterOp, that) + override def do_<= (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, LessEqOp, that) + override def do_>= (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, GreaterEqOp, that) + + @chiselRuntimeDeprecated + @deprecated("Use '=/=', which avoids potential precedence problems", "3.0") + final def != (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = this =/= that + + /** Dynamic not equals operator + * + * @param that a hardware $coll + * @return a hardware [[Bool]] asserted if this $coll is not equal to `that` + * @group Comparison + */ + final def =/= (that: UInt): Bool = macro SourceInfoTransform.thatArg + + /** Dynamic equals operator + * + * @param that a hardware $coll + * @return a hardware [[Bool]] asserted if this $coll is equal to `that` + * @group Comparison + */ + final def === (that: UInt): Bool = macro SourceInfoTransform.thatArg + + /** @group SourceInfoTransformMacro */ + def do_=/= (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, NotEqualOp, that) + /** @group SourceInfoTransformMacro */ + def do_=== (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, EqualOp, that) + + /** Unary not + * + * @return a hardware [[Bool]] asserted if this $coll equals zero + * @group Bitwise + */ + final def unary_! () : Bool = macro SourceInfoTransform.noArg + + /** @group SourceInfoTransformMacro */ + def do_unary_! (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions) : Bool = this === 0.U(1.W) + + override def do_<< (that: Int)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = + binop(sourceInfo, UInt(this.width + that), ShiftLeftOp, validateShiftAmount(that)) + override def do_<< (that: BigInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = + this << castToInt(that, "Shift amount") + override def do_<< (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = + binop(sourceInfo, UInt(this.width.dynamicShiftLeft(that.width)), DynamicShiftLeftOp, that) + override def do_>> (that: Int)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = + binop(sourceInfo, UInt(this.width.shiftRight(that)), ShiftRightOp, validateShiftAmount(that)) + override def do_>> (that: BigInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = + this >> castToInt(that, "Shift amount") + override def do_>> (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = + binop(sourceInfo, UInt(this.width), DynamicShiftRightOp, that) + + /** Conditionally set or clear a bit + * + * @param off a dynamic offset + * @param dat set if true, clear if false + * @return a hrdware $coll with bit `off` set or cleared based on the value of `dat` + * $unchangedWidth + */ + final def bitSet(off: UInt, dat: Bool): UInt = macro UIntTransform.bitset + + /** @group SourceInfoTransformMacro */ + def do_bitSet(off: UInt, dat: Bool)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = { + val bit = 1.U(1.W) << off + Mux(dat, this | bit, ~(~this | bit)) + } + + // TODO: this eventually will be renamed as toSInt, once the existing toSInt + // completes its deprecation phase. + /** Zero extend as [[SInt]] + * + * @return an [[SInt]] equal to this $coll with an additional zero in its most significant bit + * @note The width of the returned [[SInt]] is `width of this` + `1`. + */ + final def zext(): SInt = macro SourceInfoTransform.noArg + /** @group SourceInfoTransformMacro */ + def do_zext(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = + pushOp(DefPrim(sourceInfo, SInt(width + 1), ConvertOp, ref)) + + override def do_asSInt(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = + pushOp(DefPrim(sourceInfo, SInt(width), AsSIntOp, ref)) + override def do_asUInt(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = this + override def do_asFixedPoint(binaryPoint: BinaryPoint)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = { + binaryPoint match { + case KnownBinaryPoint(value) => + val iLit = ILit(value) + pushOp(DefPrim(sourceInfo, FixedPoint(width, binaryPoint), AsFixedPointOp, ref, iLit)) + case _ => + throwException(s"cannot call $this.asFixedPoint(binaryPoint=$binaryPoint), you must specify a known binaryPoint") + } + } + + override def do_asInterval(range: IntervalRange = IntervalRange.Unknown) + (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { + (range.lower, range.upper, range.binaryPoint) match { + case (lx: firrtlconstraint.IsKnown, ux: firrtlconstraint.IsKnown, KnownBinaryPoint(bp)) => + // No mechanism to pass open/close to firrtl so need to handle directly + val l = lx match { + case firrtlir.Open(x) => x + BigDecimal(1) / BigDecimal(BigInt(1) << bp) + case firrtlir.Closed(x) => x + } + val u = ux match { + case firrtlir.Open(x) => x - BigDecimal(1) / BigDecimal(BigInt(1) << bp) + case firrtlir.Closed(x) => x + } + val minBI = (l * BigDecimal(BigInt(1) << bp)).setScale(0, BigDecimal.RoundingMode.FLOOR).toBigIntExact.get + val maxBI = (u * BigDecimal(BigInt(1) << bp)).setScale(0, BigDecimal.RoundingMode.FLOOR).toBigIntExact.get + pushOp(DefPrim(sourceInfo, Interval(range), AsIntervalOp, ref, ILit(minBI), ILit(maxBI), ILit(bp))) + case _ => + throwException( + s"cannot call $this.asInterval($range), you must specify a known binaryPoint and range") + } + } + private[chisel3] override def connectFromBits(that: Bits)(implicit sourceInfo: SourceInfo, + compileOptions: CompileOptions): Unit = { + this := that.asUInt + } + + private def subtractAsSInt(that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = + binop(sourceInfo, SInt((this.width max that.width) + 1), SubOp, that) +} + +/** A data type for signed integers, represented as a binary bitvector. Defines arithmetic operations between other + * integer types. + * + * @define coll [[SInt]] + * @define numType $coll + * @define expandingWidth @note The width of the returned $coll is `width of this` + `1`. + * @define constantWidth @note The width of the returned $coll is unchanged, i.e., `width of this`. + */ +sealed class SInt private[chisel3] (width: Width) extends Bits(width) with Num[SInt] { + override def toString: String = { + val bindingString = litOption match { + case Some(value) => s"($value)" + case _ => bindingToString + } + s"SInt$width$bindingString" + } + + private[chisel3] override def typeEquivalent(that: Data): Boolean = + this.getClass == that.getClass && this.width == that.width // TODO: should this be true for unspecified widths? + + private[chisel3] override def cloneTypeWidth(w: Width): this.type = + new SInt(w).asInstanceOf[this.type] + + /** Unary negation (expanding width) + * + * @return a hardware $coll equal to zero minus this $coll + * $constantWidth + * @group Arithmetic + */ + final def unary_- (): SInt = macro SourceInfoTransform.noArg + + /** Unary negation (constant width) + * + * @return a hardware $coll equal to zero minus `this` shifted right by one + * $constantWidth + * @group Arithmetic + */ + final def unary_-% (): SInt = macro SourceInfoTransform.noArg + + /** @group SourceInfoTransformMacro */ + def unary_- (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = 0.S - this + /** @group SourceInfoTransformMacro */ + def unary_-% (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = 0.S -% this + + /** add (default - no growth) operator */ + override def do_+ (that: SInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = + this +% that + /** subtract (default - no growth) operator */ + override def do_- (that: SInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = + this -% that + override def do_* (that: SInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = + binop(sourceInfo, SInt(this.width + that.width), TimesOp, that) + override def do_/ (that: SInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = + binop(sourceInfo, SInt(this.width), DivideOp, that) + override def do_% (that: SInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = + binop(sourceInfo, SInt(this.width), RemOp, that) + + /** Multiplication operator + * + * @param that a hardware $coll + * @return the product of this $coll and `that` + * $sumWidth + * $singleCycleMul + * @group Arithmetic + */ + final def * (that: UInt): SInt = macro SourceInfoTransform.thatArg + /** @group SourceInfoTransformMacro */ + def do_* (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = { + val thatToSInt = that.zext() + val result = binop(sourceInfo, SInt(this.width + thatToSInt.width), TimesOp, thatToSInt) + result.tail(1).asSInt + } + + /** Addition operator (expanding width) + * + * @param that a hardware $coll + * @return the sum of this $coll and `that` + * $maxWidthPlusOne + * @group Arithmetic + */ + final def +& (that: SInt): SInt = macro SourceInfoTransform.thatArg + + /** Addition operator (constant width) + * + * @param that a hardware $coll + * @return the sum of this $coll and `that` shifted right by one + * $maxWidth + * @group Arithmetic + */ + final def +% (that: SInt): SInt = macro SourceInfoTransform.thatArg + + /** Subtraction operator (increasing width) + * + * @param that a hardware $coll + * @return the difference of this $coll less `that` + * $maxWidthPlusOne + * @group Arithmetic + */ + final def -& (that: SInt): SInt = macro SourceInfoTransform.thatArg + + /** Subtraction operator (constant width) + * + * @param that a hardware $coll + * @return the difference of this $coll less `that` shifted right by one + * $maxWidth + * @group Arithmetic + */ + final def -% (that: SInt): SInt = macro SourceInfoTransform.thatArg + + /** @group SourceInfoTransformMacro */ + def do_+& (that: SInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = + binop(sourceInfo, SInt((this.width max that.width) + 1), AddOp, that) + /** @group SourceInfoTransformMacro */ + def do_+% (that: SInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = + (this +& that).tail(1).asSInt + /** @group SourceInfoTransformMacro */ + def do_-& (that: SInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = + binop(sourceInfo, SInt((this.width max that.width) + 1), SubOp, that) + /** @group SourceInfoTransformMacro */ + def do_-% (that: SInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = + (this -& that).tail(1).asSInt + + /** Bitwise and operator + * + * @param that a hardware $coll + * @return the bitwise and of this $coll and `that` + * $maxWidth + * @group Bitwise + */ + final def & (that: SInt): SInt = macro SourceInfoTransform.thatArg + + /** Bitwise or operator + * + * @param that a hardware $coll + * @return the bitwise or of this $coll and `that` + * $maxWidth + * @group Bitwise + */ + final def | (that: SInt): SInt = macro SourceInfoTransform.thatArg + + /** Bitwise exclusive or (xor) operator + * + * @param that a hardware $coll + * @return the bitwise xor of this $coll and `that` + * $maxWidth + * @group Bitwise + */ + final def ^ (that: SInt): SInt = macro SourceInfoTransform.thatArg + + /** @group SourceInfoTransformMacro */ + def do_& (that: SInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = + binop(sourceInfo, UInt(this.width max that.width), BitAndOp, that).asSInt + /** @group SourceInfoTransformMacro */ + def do_| (that: SInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = + binop(sourceInfo, UInt(this.width max that.width), BitOrOp, that).asSInt + /** @group SourceInfoTransformMacro */ + def do_^ (that: SInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = + binop(sourceInfo, UInt(this.width max that.width), BitXorOp, that).asSInt + + /** @group SourceInfoTransformMacro */ + def do_unary_~ (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = + unop(sourceInfo, UInt(width = width), BitNotOp).asSInt + + override def do_< (that: SInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, LessOp, that) + override def do_> (that: SInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, GreaterOp, that) + override def do_<= (that: SInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, LessEqOp, that) + override def do_>= (that: SInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, GreaterEqOp, that) + + @chiselRuntimeDeprecated + @deprecated("Use '=/=', which avoids potential precedence problems", "3.0") + final def != (that: SInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = this =/= that + + /** Dynamic not equals operator + * + * @param that a hardware $coll + * @return a hardware [[Bool]] asserted if this $coll is not equal to `that` + * @group Comparison + */ + final def =/= (that: SInt): Bool = macro SourceInfoTransform.thatArg + + /** Dynamic equals operator + * + * @param that a hardware $coll + * @return a hardware [[Bool]] asserted if this $coll is equal to `that` + * @group Comparison + */ + final def === (that: SInt): Bool = macro SourceInfoTransform.thatArg + + /** @group SourceInfoTransformMacro */ + def do_=/= (that: SInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, NotEqualOp, that) + /** @group SourceInfoTransformMacro */ + def do_=== (that: SInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, EqualOp, that) + +// final def abs(): UInt = macro SourceInfoTransform.noArg + + def do_abs(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = { + Mux(this < 0.S, (-this), this) + } + + override def do_<< (that: Int)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = + binop(sourceInfo, SInt(this.width + that), ShiftLeftOp, validateShiftAmount(that)) + override def do_<< (that: BigInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = + this << castToInt(that, "Shift amount") + override def do_<< (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = + binop(sourceInfo, SInt(this.width.dynamicShiftLeft(that.width)), DynamicShiftLeftOp, that) + override def do_>> (that: Int)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = + binop(sourceInfo, SInt(this.width.shiftRight(that)), ShiftRightOp, validateShiftAmount(that)) + override def do_>> (that: BigInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = + this >> castToInt(that, "Shift amount") + override def do_>> (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = + binop(sourceInfo, SInt(this.width), DynamicShiftRightOp, that) + + override def do_asUInt(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = pushOp(DefPrim(sourceInfo, UInt(this.width), AsUIntOp, ref)) + override def do_asSInt(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = this + override def do_asFixedPoint(binaryPoint: BinaryPoint)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = { + binaryPoint match { + case KnownBinaryPoint(value) => + val iLit = ILit(value) + pushOp(DefPrim(sourceInfo, FixedPoint(width, binaryPoint), AsFixedPointOp, ref, iLit)) + case _ => + throwException(s"cannot call $this.asFixedPoint(binaryPoint=$binaryPoint), you must specify a known binaryPoint") + } + } + + override def do_asInterval(range: IntervalRange = IntervalRange.Unknown) + (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { + (range.lower, range.upper, range.binaryPoint) match { + case (lx: firrtlconstraint.IsKnown, ux: firrtlconstraint.IsKnown, KnownBinaryPoint(bp)) => + // No mechanism to pass open/close to firrtl so need to handle directly + val l = lx match { + case firrtlir.Open(x) => x + BigDecimal(1) / BigDecimal(BigInt(1) << bp) + case firrtlir.Closed(x) => x + } + val u = ux match { + case firrtlir.Open(x) => x - BigDecimal(1) / BigDecimal(BigInt(1) << bp) + case firrtlir.Closed(x) => x + } + //TODO: (chick) Need to determine, what asInterval needs, and why it might need min and max as args -- CAN IT BE UNKNOWN? + // Angie's operation: Decimal -> Int -> Decimal loses information. Need to be conservative here? + val minBI = (l * BigDecimal(BigInt(1) << bp)).setScale(0, BigDecimal.RoundingMode.FLOOR).toBigIntExact.get + val maxBI = (u * BigDecimal(BigInt(1) << bp)).setScale(0, BigDecimal.RoundingMode.FLOOR).toBigIntExact.get + pushOp(DefPrim(sourceInfo, Interval(range), AsIntervalOp, ref, ILit(minBI), ILit(maxBI), ILit(bp))) + case _ => + throwException( + s"cannot call $this.asInterval($range), you must specify a known binaryPoint and range") + } + } + + private[chisel3] override def connectFromBits(that: Bits)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions) { + this := that.asSInt + } +} + +sealed trait Reset extends Element with ToBoolable { + /** Casts this $coll to an [[AsyncReset]] */ + final def asAsyncReset(): AsyncReset = macro SourceInfoWhiteboxTransform.noArg + + /** @group SourceInfoTransformMacro */ + def do_asAsyncReset(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): AsyncReset +} + +object Reset { + def apply(): Reset = new ResetType +} + +/** "Abstract" Reset Type inferred in FIRRTL to either [[AsyncReset]] or [[Bool]] + * + * @note This shares a common interface with [[AsyncReset]] and [[Bool]] but is not their actual + * super type due to Bool inheriting from abstract class UInt + */ +final class ResetType(private[chisel3] val width: Width = Width(1)) extends Element with Reset { + override def toString: String = s"Reset$bindingToString" + + def cloneType: this.type = Reset().asInstanceOf[this.type] + + private[chisel3] def typeEquivalent(that: Data): Boolean = + this.getClass == that.getClass + + override def connect(that: Data)(implicit sourceInfo: SourceInfo, connectCompileOptions: CompileOptions): Unit = that match { + case _: Reset | DontCare => super.connect(that)(sourceInfo, connectCompileOptions) + case _ => super.badConnect(that)(sourceInfo) + } + + override def litOption = None + + /** Not really supported */ + def toPrintable: Printable = PString("Reset") + + override def do_asUInt(implicit sourceInfo: SourceInfo, connectCompileOptions: CompileOptions): UInt = pushOp(DefPrim(sourceInfo, UInt(this.width), AsUIntOp, ref)) + + private[chisel3] override def connectFromBits(that: Bits)(implicit sourceInfo: SourceInfo, + compileOptions: CompileOptions): Unit = { + this := that + } + + /** @group SourceInfoTransformMacro */ + def do_asAsyncReset(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): AsyncReset = + pushOp(DefPrim(sourceInfo, AsyncReset(), AsAsyncResetOp, ref)) + + /** @group SourceInfoTransformMacro */ + def do_asBool(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = + pushOp(DefPrim(sourceInfo, Bool(), AsUIntOp, ref)) + + /** @group SourceInfoTransformMacro */ + def do_toBool(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = do_asBool +} + +object AsyncReset { + def apply(): AsyncReset = new AsyncReset +} + +/** Data type representing asynchronous reset signals + * + * These signals are similar to [[Clock]]s in that they must be glitch-free for proper circuit + * operation. [[Reg]]s defined with the implicit reset being an [[AsyncReset]] will be + * asychronously reset registers. + */ +sealed class AsyncReset(private[chisel3] val width: Width = Width(1)) extends Element with Reset { + override def toString: String = s"AsyncReset$bindingToString" + + def cloneType: this.type = AsyncReset().asInstanceOf[this.type] + + private[chisel3] def typeEquivalent(that: Data): Boolean = + this.getClass == that.getClass + + override def connect(that: Data)(implicit sourceInfo: SourceInfo, connectCompileOptions: CompileOptions): Unit = that match { + case _: AsyncReset | DontCare => super.connect(that)(sourceInfo, connectCompileOptions) + case _ => super.badConnect(that)(sourceInfo) + } + + override def litOption = None + + /** Not really supported */ + def toPrintable: Printable = PString("AsyncReset") + + override def do_asUInt(implicit sourceInfo: SourceInfo, connectCompileOptions: CompileOptions): UInt = pushOp(DefPrim(sourceInfo, UInt(this.width), AsUIntOp, ref)) + + // TODO Is this right? + private[chisel3] override def connectFromBits(that: Bits)(implicit sourceInfo: SourceInfo, + compileOptions: CompileOptions): Unit = { + this := that.asBool.asAsyncReset + } + + /** @group SourceInfoTransformMacro */ + def do_asAsyncReset(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): AsyncReset = this + + /** @group SourceInfoTransformMacro */ + def do_asBool(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = + pushOp(DefPrim(sourceInfo, Bool(), AsUIntOp, ref)) + + /** @group SourceInfoTransformMacro */ + def do_toBool(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = do_asBool +} + +// REVIEW TODO: Why does this extend UInt and not Bits? Does defining airth +// operations on a Bool make sense? +/** A data type for booleans, defined as a single bit indicating true or false. + * + * @define coll [[Bool]] + * @define numType $coll + */ +sealed class Bool() extends UInt(1.W) with Reset { + override def toString: String = { + val bindingString = litToBooleanOption match { + case Some(value) => s"($value)" + case _ => bindingToString + } + s"Bool$bindingString" + } + + private[chisel3] override def cloneTypeWidth(w: Width): this.type = { + require(!w.known || w.get == 1) + new Bool().asInstanceOf[this.type] + } + + /** Convert to a [[scala.Option]] of [[scala.Boolean]] */ + def litToBooleanOption: Option[Boolean] = litOption.map { + case intVal if intVal == 1 => true + case intVal if intVal == 0 => false + case intVal => throwException(s"Boolean with unexpected literal value $intVal") + } + + /** Convert to a [[scala.Boolean]] */ + def litToBoolean: Boolean = litToBooleanOption.get + + // REVIEW TODO: Why does this need to exist and have different conventions + // than Bits? + + /** Bitwise and operator + * + * @param that a hardware $coll + * @return the bitwise and of this $coll and `that` + * @group Bitwise + */ + final def & (that: Bool): Bool = macro SourceInfoTransform.thatArg + + /** Bitwise or operator + * + * @param that a hardware $coll + * @return the bitwise or of this $coll and `that` + * @group Bitwise + */ + final def | (that: Bool): Bool = macro SourceInfoTransform.thatArg + + /** Bitwise exclusive or (xor) operator + * + * @param that a hardware $coll + * @return the bitwise xor of this $coll and `that` + * @group Bitwise + */ + final def ^ (that: Bool): Bool = macro SourceInfoTransform.thatArg + + /** @group SourceInfoTransformMacro */ + def do_& (that: Bool)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = + binop(sourceInfo, Bool(), BitAndOp, that) + /** @group SourceInfoTransformMacro */ + def do_| (that: Bool)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = + binop(sourceInfo, Bool(), BitOrOp, that) + /** @group SourceInfoTransformMacro */ + def do_^ (that: Bool)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = + binop(sourceInfo, Bool(), BitXorOp, that) + + /** @group SourceInfoTransformMacro */ + override def do_unary_~ (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = + unop(sourceInfo, Bool(), BitNotOp) + + /** Logical or operator + * + * @param that a hardware $coll + * @return the lgocial or of this $coll and `that` + * @note this is equivalent to [[Bool!.|(that:chisel3\.Bool)* Bool.|)]] + * @group Logical + */ + def || (that: Bool): Bool = macro SourceInfoTransform.thatArg + + /** @group SourceInfoTransformMacro */ + def do_|| (that: Bool)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = this | that + + /** Logical and operator + * + * @param that a hardware $coll + * @return the lgocial and of this $coll and `that` + * @note this is equivalent to [[Bool!.&(that:chisel3\.Bool)* Bool.&]] + * @group Logical + */ + def && (that: Bool): Bool = macro SourceInfoTransform.thatArg + + /** @group SourceInfoTransformMacro */ + def do_&& (that: Bool)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = this & that + + /** Reinterprets this $coll as a clock */ + def asClock(): Clock = macro SourceInfoTransform.noArg + + /** @group SourceInfoTransformMacro */ + def do_asClock(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Clock = pushOp(DefPrim(sourceInfo, Clock(), AsClockOp, ref)) + + /** @group SourceInfoTransformMacro */ + def do_asAsyncReset(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): AsyncReset = + pushOp(DefPrim(sourceInfo, AsyncReset(), AsAsyncResetOp, ref)) +} + +package experimental { + + import chisel3.internal.firrtl.BinaryPoint + + /** Chisel types that have binary points support retrieving + * literal values as `Double` or `BigDecimal` + */ + trait HasBinaryPoint { self: Bits => + def binaryPoint: BinaryPoint + + /** Return the [[Double]] value of this instance if it is a Literal + * @note this method may throw an exception if the literal value won't fit in a Double + */ + def litToDoubleOption: Option[Double] = { + litOption match { + case Some(bigInt: BigInt) => + Some(Num.toDouble(bigInt, binaryPoint)) + case _ => None + } + } + + /** Return the double value of this instance assuming it is a literal (convenience method) + */ + def litToDouble: Double = litToDoubleOption.get + + /** Return the [[BigDecimal]] value of this instance if it is a Literal + * @note this method may throw an exception if the literal value won't fit in a BigDecimal + */ + def litToBigDecimalOption: Option[BigDecimal] = { + litOption match { + case Some(bigInt: BigInt) => + Some(Num.toBigDecimal(bigInt, binaryPoint)) + case _ => None + } + } + + /** Return the [[BigDecimal]] value of this instance assuming it is a literal (convenience method) + * @return + */ + def litToBigDecimal: BigDecimal = litToBigDecimalOption.get + } + //scalastyle:off number.of.methods + /** A sealed class representing a fixed point number that has a bit width and a binary point The width and binary point + * may be inferred. + * + * IMPORTANT: The API provided here is experimental and may change in the future. + * + * @param width bit width of the fixed point number + * @param binaryPoint the position of the binary point with respect to the right most bit of the width currently this + * should be positive but it is hoped to soon support negative points and thus use this field as a + * simple exponent + * @define coll [[FixedPoint]] + * @define numType $coll + * @define expandingWidth @note The width of the returned $coll is `width of this` + `1`. + * @define constantWidth @note The width of the returned $coll is unchanged, i.e., `width of this`. + */ + sealed class FixedPoint private(width: Width, val binaryPoint: BinaryPoint) + extends Bits(width) with Num[FixedPoint] with HasBinaryPoint { + + override def toString: String = { + val bindingString = litToDoubleOption match { + case Some(value) => s"($value)" + case _ => bindingToString + } + s"FixedPoint$width$binaryPoint$bindingString" + } + + private[chisel3] override def typeEquivalent(that: Data): Boolean = that match { + case that: FixedPoint => this.width == that.width && this.binaryPoint == that.binaryPoint // TODO: should this be true for unspecified widths? + case _ => false + } + + private[chisel3] override def cloneTypeWidth(w: Width): this.type = + new FixedPoint(w, binaryPoint).asInstanceOf[this.type] + + override def connect(that: Data)(implicit sourceInfo: SourceInfo, connectCompileOptions: CompileOptions): Unit = that match { + case _: FixedPoint|DontCare => super.connect(that) + case _ => this badConnect that + } + + /** Unary negation (expanding width) + * + * @return a hardware $coll equal to zero minus this $coll + * $expandingWidth + * @group Arithmetic + */ + final def unary_- (): FixedPoint = macro SourceInfoTransform.noArg + + /** Unary negation (constant width) + * + * @return a hardware $coll equal to zero minus `this` shifted right by one + * $constantWidth + * @group Arithmetic + */ + final def unary_-% (): FixedPoint = macro SourceInfoTransform.noArg + + /** @group SourceInfoTransformMacro */ + def unary_- (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = FixedPoint.fromBigInt(0) - this + /** @group SourceInfoTransformMacro */ + def unary_-% (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = FixedPoint.fromBigInt(0) -% this + + /** add (default - no growth) operator */ + override def do_+ (that: FixedPoint)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = + this +% that + /** subtract (default - no growth) operator */ + override def do_- (that: FixedPoint)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = + this -% that + override def do_* (that: FixedPoint)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = + binop(sourceInfo, FixedPoint(this.width + that.width, this.binaryPoint + that.binaryPoint), TimesOp, that) + override def do_/ (that: FixedPoint)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = + throwException(s"division is illegal on FixedPoint types") + override def do_% (that: FixedPoint)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = + throwException(s"mod is illegal on FixedPoint types") + + + /** Multiplication operator + * + * @param that a hardware [[UInt]] + * @return the product of this $coll and `that` + * $sumWidth + * $singleCycleMul + * @group Arithmetic + */ + final def * (that: UInt): FixedPoint = macro SourceInfoTransform.thatArg + /** @group SourceInfoTransformMacro */ + def do_* (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = + binop(sourceInfo, FixedPoint(this.width + that.width, binaryPoint), TimesOp, that) + + /** Multiplication operator + * + * @param that a hardware [[SInt]] + * @return the product of this $coll and `that` + * $sumWidth + * $singleCycleMul + * @group Arithmetic + */ + final def * (that: SInt): FixedPoint = macro SourceInfoTransform.thatArg + /** @group SourceInfoTransformMacro */ + def do_* (that: SInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = + binop(sourceInfo, FixedPoint(this.width + that.width, binaryPoint), TimesOp, that) + + /** Addition operator (expanding width) + * + * @param that a hardware $coll + * @return the sum of this $coll and `that` + * $maxWidthPlusOne + * @group Arithmetic + */ + final def +& (that: FixedPoint): FixedPoint = macro SourceInfoTransform.thatArg + + /** Addition operator (constant width) + * + * @param that a hardware $coll + * @return the sum of this $coll and `that` shifted right by one + * $maxWidth + * @group Arithmetic + */ + final def +% (that: FixedPoint): FixedPoint = macro SourceInfoTransform.thatArg + + /** Subtraction operator (increasing width) + * + * @param that a hardware $coll + * @return the difference of this $coll less `that` + * $maxWidthPlusOne + * @group Arithmetic + */ + final def -& (that: FixedPoint): FixedPoint = macro SourceInfoTransform.thatArg + + /** Subtraction operator (constant width) + * + * @param that a hardware $coll + * @return the difference of this $coll less `that` shifted right by one + * $maxWidth + * @group Arithmetic + */ + final def -% (that: FixedPoint): FixedPoint = macro SourceInfoTransform.thatArg + + /** @group SourceInfoTransformMacro */ + def do_+& (that: FixedPoint)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = { + (this.width, that.width, this.binaryPoint, that.binaryPoint) match { + case (KnownWidth(thisWidth), KnownWidth(thatWidth), KnownBinaryPoint(thisBP), KnownBinaryPoint(thatBP)) => + val thisIntWidth = thisWidth - thisBP + val thatIntWidth = thatWidth - thatBP + val newBinaryPoint = thisBP max thatBP + val newWidth = (thisIntWidth max thatIntWidth) + newBinaryPoint + 1 + binop(sourceInfo, FixedPoint(newWidth.W, newBinaryPoint.BP), AddOp, that) + case _ => + val newBinaryPoint = this.binaryPoint max that.binaryPoint + binop(sourceInfo, FixedPoint(UnknownWidth(), newBinaryPoint), AddOp, that) + } + } + + /** @group SourceInfoTransformMacro */ + def do_+% (that: FixedPoint)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = + (this +& that).tail(1).asFixedPoint(this.binaryPoint max that.binaryPoint) + /** @group SourceInfoTransformMacro */ + def do_-& (that: FixedPoint)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = { + (this.width, that.width, this.binaryPoint, that.binaryPoint) match { + case (KnownWidth(thisWidth), KnownWidth(thatWidth), KnownBinaryPoint(thisBP), KnownBinaryPoint(thatBP)) => + val thisIntWidth = thisWidth - thisBP + val thatIntWidth = thatWidth - thatBP + val newBinaryPoint = thisBP max thatBP + val newWidth = (thisIntWidth max thatIntWidth) + newBinaryPoint + 1 + binop(sourceInfo, FixedPoint(newWidth.W, newBinaryPoint.BP), SubOp, that) + case _ => + val newBinaryPoint = this.binaryPoint max that.binaryPoint + binop(sourceInfo, FixedPoint(UnknownWidth(), newBinaryPoint), SubOp, that) + } + } + + /** @group SourceInfoTransformMacro */ + def do_-% (that: FixedPoint)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = + (this -& that).tail(1).asFixedPoint(this.binaryPoint max that.binaryPoint) + + /** Bitwise and operator + * + * @param that a hardware $coll + * @return the bitwise and of this $coll and `that` + * $maxWidth + * @group Bitwise + */ + final def & (that: FixedPoint): FixedPoint = macro SourceInfoTransform.thatArg + + /** Bitwise or operator + * + * @param that a hardware $coll + * @return the bitwise or of this $coll and `that` + * $maxWidth + * @group Bitwise + */ + final def | (that: FixedPoint): FixedPoint = macro SourceInfoTransform.thatArg + + /** Bitwise exclusive or (xor) operator + * + * @param that a hardware $coll + * @return the bitwise xor of this $coll and `that` + * $maxWidth + * @group Bitwise + */ + final def ^ (that: FixedPoint): FixedPoint = macro SourceInfoTransform.thatArg + + /** @group SourceInfoTransformMacro */ + def do_& (that: FixedPoint)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = + throwException(s"And is illegal between $this and $that") + /** @group SourceInfoTransformMacro */ + def do_| (that: FixedPoint)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = + throwException(s"Or is illegal between $this and $that") + /** @group SourceInfoTransformMacro */ + def do_^ (that: FixedPoint)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = + throwException(s"Xor is illegal between $this and $that") + + final def setBinaryPoint(that: Int): FixedPoint = macro SourceInfoTransform.thatArg + + /** @group SourceInfoTransformMacro */ + def do_setBinaryPoint(that: Int)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = this.binaryPoint match { + case KnownBinaryPoint(value) => + binop(sourceInfo, FixedPoint(this.width + (that - value), KnownBinaryPoint(that)), SetBinaryPoint, that) + case _ => + binop(sourceInfo, FixedPoint(UnknownWidth(), KnownBinaryPoint(that)), SetBinaryPoint, that) + } + + /** @group SourceInfoTransformMacro */ + def do_unary_~ (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = + throwException(s"Not is illegal on $this") + + override def do_< (that: FixedPoint)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, LessOp, that) + override def do_> (that: FixedPoint)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, GreaterOp, that) + override def do_<= (that: FixedPoint)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, LessEqOp, that) + override def do_>= (that: FixedPoint)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, GreaterEqOp, that) + + final def != (that: FixedPoint): Bool = macro SourceInfoTransform.thatArg + + /** Dynamic not equals operator + * + * @param that a hardware $coll + * @return a hardware [[Bool]] asserted if this $coll is not equal to `that` + * @group Comparison + */ + final def =/= (that: FixedPoint): Bool = macro SourceInfoTransform.thatArg + + /** Dynamic equals operator + * + * @param that a hardware $coll + * @return a hardware [[Bool]] asserted if this $coll is equal to `that` + * @group Comparison + */ + final def === (that: FixedPoint): Bool = macro SourceInfoTransform.thatArg + + /** @group SourceInfoTransformMacro */ + def do_!= (that: FixedPoint)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, NotEqualOp, that) + /** @group SourceInfoTransformMacro */ + def do_=/= (that: FixedPoint)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, NotEqualOp, that) + /** @group SourceInfoTransformMacro */ + def do_=== (that: FixedPoint)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, EqualOp, that) + + def do_abs(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = { + // TODO: remove this once we have CompileOptions threaded through the macro system. + import chisel3.ExplicitCompileOptions.NotStrict + Mux(this < 0.F(0.BP), 0.F(0.BP) - this, this) + } + + override def do_<< (that: Int)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = + binop(sourceInfo, FixedPoint(this.width + that, this.binaryPoint), ShiftLeftOp, validateShiftAmount(that)) + override def do_<< (that: BigInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = + (this << castToInt(that, "Shift amount")).asFixedPoint(this.binaryPoint) + override def do_<< (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = + binop(sourceInfo, FixedPoint(this.width.dynamicShiftLeft(that.width), this.binaryPoint), DynamicShiftLeftOp, that) + override def do_>> (that: Int)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = + binop(sourceInfo, FixedPoint(this.width.shiftRight(that), this.binaryPoint), ShiftRightOp, validateShiftAmount(that)) + override def do_>> (that: BigInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = + (this >> castToInt(that, "Shift amount")).asFixedPoint(this.binaryPoint) + override def do_>> (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = + binop(sourceInfo, FixedPoint(this.width, this.binaryPoint), DynamicShiftRightOp, that) + + override def do_asUInt(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = pushOp(DefPrim(sourceInfo, UInt(this.width), AsUIntOp, ref)) + override def do_asSInt(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = pushOp(DefPrim(sourceInfo, SInt(this.width), AsSIntOp, ref)) + + override def do_asFixedPoint(binaryPoint: BinaryPoint)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = { + binaryPoint match { + case KnownBinaryPoint(value) => + val iLit = ILit(value) + pushOp(DefPrim(sourceInfo, FixedPoint(width, binaryPoint), AsFixedPointOp, ref, iLit)) + case _ => + throwException(s"cannot call $this.asFixedPoint(binaryPoint=$binaryPoint), you must specify a known binaryPoint") + } + } + + def do_asInterval(binaryPoint: BinaryPoint)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { + throwException(s"cannot call $this.asInterval(binaryPoint=$binaryPoint), you must specify a range") + } + + override def do_asInterval(range: IntervalRange = IntervalRange.Unknown) + (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { + (range.lower, range.upper, range.binaryPoint) match { + case (lx: firrtlconstraint.IsKnown, ux: firrtlconstraint.IsKnown, KnownBinaryPoint(bp)) => + // No mechanism to pass open/close to firrtl so need to handle directly + val l = lx match { + case firrtlir.Open(x) => x + BigDecimal(1) / BigDecimal(BigInt(1) << bp) + case firrtlir.Closed(x) => x + } + val u = ux match { + case firrtlir.Open(x) => x - BigDecimal(1) / BigDecimal(BigInt(1) << bp) + case firrtlir.Closed(x) => x + } + val minBI = (l * BigDecimal(BigInt(1) << bp)).setScale(0, BigDecimal.RoundingMode.FLOOR).toBigIntExact.get + val maxBI = (u * BigDecimal(BigInt(1) << bp)).setScale(0, BigDecimal.RoundingMode.FLOOR).toBigIntExact.get + pushOp(DefPrim(sourceInfo, Interval(range), AsIntervalOp, ref, ILit(minBI), ILit(maxBI), ILit(bp))) + case _ => + throwException( + s"cannot call $this.asInterval($range), you must specify a known binaryPoint and range") + } + } + + private[chisel3] override def connectFromBits(that: Bits)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions) { + // TODO: redefine as just asFixedPoint on that, where FixedPoint.asFixedPoint just works. + this := (that match { + case fp: FixedPoint => fp.asSInt.asFixedPoint(this.binaryPoint) + case _ => that.asFixedPoint(this.binaryPoint) + }) + } + } + + /** Use PrivateObject to force users to specify width and binaryPoint by name + */ + sealed trait PrivateType + private case object PrivateObject extends PrivateType + + /** + * Factory and convenience methods for the FixedPoint class + * IMPORTANT: The API provided here is experimental and may change in the future. + */ + object FixedPoint extends NumObject { + + import FixedPoint.Implicits._ + + /** Create an FixedPoint type with inferred width. */ + def apply(): FixedPoint = apply(Width(), BinaryPoint()) + + /** Create an FixedPoint type or port with fixed width. */ + def apply(width: Width, binaryPoint: BinaryPoint): FixedPoint = new FixedPoint(width, binaryPoint) + + /** Create an FixedPoint literal with inferred width from BigInt. + * Use PrivateObject to force users to specify width and binaryPoint by name + */ + def fromBigInt(value: BigInt, width: Width, binaryPoint: BinaryPoint): FixedPoint = { + apply(value, width, binaryPoint) + } + /** Create an FixedPoint literal with inferred width from BigInt. + * Use PrivateObject to force users to specify width and binaryPoint by name + */ + def fromBigInt(value: BigInt, binaryPoint: BinaryPoint = 0.BP): FixedPoint = { + apply(value, Width(), binaryPoint) + } + /** Create an FixedPoint literal with inferred width from BigInt. + * Use PrivateObject to force users to specify width and binaryPoint by name + */ + def fromBigInt(value: BigInt, width: Int, binaryPoint: Int): FixedPoint = + if(width == -1) { + apply(value, Width(), BinaryPoint(binaryPoint)) + } + else { + apply(value, Width(width), BinaryPoint(binaryPoint)) + } + /** Create an FixedPoint literal with inferred width from Double. + * Use PrivateObject to force users to specify width and binaryPoint by name + */ + def fromDouble(value: Double, width: Width, binaryPoint: BinaryPoint): FixedPoint = { + fromBigInt( + toBigInt(value, binaryPoint.get), width = width, binaryPoint = binaryPoint + ) + } + /** Create an FixedPoint literal with inferred width from BigDecimal. + * Use PrivateObject to force users to specify width and binaryPoint by name + */ + def fromBigDecimal(value: BigDecimal, width: Width, binaryPoint: BinaryPoint): FixedPoint = { + fromBigInt( + toBigInt(value, binaryPoint.get), width = width, binaryPoint = binaryPoint + ) + } + + /** Create an FixedPoint port with specified width and binary position. */ + def apply(value: BigInt, width: Width, binaryPoint: BinaryPoint): FixedPoint = { + val lit = FPLit(value, width, binaryPoint) + val newLiteral = new FixedPoint(lit.width, lit.binaryPoint) + // Ensure we have something capable of generating a name. + lit.bindLitArg(newLiteral) + } + + + + object Implicits { + + implicit class fromDoubleToLiteral(double: Double) { + def F(binaryPoint: BinaryPoint): FixedPoint = { + FixedPoint.fromDouble(double, Width(), binaryPoint) + } + + def F(width: Width, binaryPoint: BinaryPoint): FixedPoint = { + FixedPoint.fromDouble(double, width, binaryPoint) + } + } + + implicit class fromBigDecimalToLiteral(bigDecimal: BigDecimal) { + def F(binaryPoint: BinaryPoint): FixedPoint = { + FixedPoint.fromBigDecimal(bigDecimal, Width(), binaryPoint) + } + + def F(width: Width, binaryPoint: BinaryPoint): FixedPoint = { + FixedPoint.fromBigDecimal(bigDecimal, width, binaryPoint) + } + } + } + } + + //scalastyle:off number.of.methods cyclomatic.complexity + /** + * A sealed class representing a fixed point number that has a range, an additional + * parameter that can determine a minimum and maximum supported value. + * The range can be used to reduce the required widths particularly in primitive + * operations with other Intervals, the canonical example being + * {{{ + * val one = 1.I + * val six = Seq.fill(6)(one).reduce(_ + _) + * }}} + * A UInt computed in this way would require a [[Width]] + * binary point + * The width and binary point may be inferred. + * + * IMPORTANT: The API provided here is experimental and may change in the future. + * + * @param range a range specifies min, max and binary point + */ + sealed class Interval private[chisel3] (val range: chisel3.internal.firrtl.IntervalRange) + extends Bits(range.getWidth) with Num[Interval] with HasBinaryPoint { + + override def toString: String = { + val bindingString = litOption match { + case Some(value) => s"($value)" + case _ => bindingToString + } + s"Interval$width$bindingString" + } + + private[chisel3] override def cloneTypeWidth(w: Width): this.type = + new Interval(range).asInstanceOf[this.type] + + //scalastyle:off cyclomatic.complexity + def toType: String = { + val zdec1 = """([+\-]?[0-9]\d*)(\.[0-9]*[1-9])(0*)""".r + val zdec2 = """([+\-]?[0-9]\d*)(\.0*)""".r + val dec = """([+\-]?[0-9]\d*)(\.[0-9]\d*)""".r + val int = """([+\-]?[0-9]\d*)""".r + def dec2string(v: BigDecimal): String = v.toString match { + case zdec1(x, y, z) => x + y + case zdec2(x, y) => x + case other => other + } + + val lowerString = range.lower match { + case firrtlir.Open(l) => s"(${dec2string(l)}, " + case firrtlir.Closed(l) => s"[${dec2string(l)}, " + case firrtlir.UnknownBound => s"[?, " + case _ => s"[?, " + } + val upperString = range.upper match { + case firrtlir.Open(u) => s"${dec2string(u)})" + case firrtlir.Closed(u) => s"${dec2string(u)}]" + case firrtlir.UnknownBound => s"?]" + case _ => s"?]" + } + val bounds = lowerString + upperString + + val pointString = range.binaryPoint match { + case KnownBinaryPoint(i) => "." + i.toString + case _ => "" + } + "Interval" + bounds + pointString + } + + private[chisel3] override def typeEquivalent(that: Data): Boolean = + that.isInstanceOf[Interval] && this.width == that.width + + def binaryPoint: BinaryPoint = range.binaryPoint + + override def connect(that: Data)(implicit sourceInfo: SourceInfo, connectCompileOptions: CompileOptions): Unit = { + that match { + case _: Interval|DontCare => super.connect(that) + case _ => this badConnect that + } + } + + final def unary_-(): Interval = macro SourceInfoTransform.noArg + final def unary_-%(): Interval = macro SourceInfoTransform.noArg + + def unary_-(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { + Interval.Zero - this + } + def unary_-%(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { + Interval.Zero -% this + } + + /** add (default - growing) operator */ + override def do_+(that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = + this +& that + /** subtract (default - growing) operator */ + override def do_-(that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = + this -& that + override def do_*(that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = + binop(sourceInfo, Interval(this.range * that.range), TimesOp, that) + + override def do_/(that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = + throwException(s"division is illegal on Interval types") + override def do_%(that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = + throwException(s"mod is illegal on Interval types") + + /** add (width +1) operator */ + final def +&(that: Interval): Interval = macro SourceInfoTransform.thatArg + /** add (no growth) operator */ + final def +%(that: Interval): Interval = macro SourceInfoTransform.thatArg + /** subtract (width +1) operator */ + final def -&(that: Interval): Interval = macro SourceInfoTransform.thatArg + /** subtract (no growth) operator */ + final def -%(that: Interval): Interval = macro SourceInfoTransform.thatArg + + def do_+&(that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { + binop(sourceInfo, Interval(this.range +& that.range), AddOp, that) + } + + def do_+%(that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { + throwException(s"Non-growing addition is not supported on Intervals: ${sourceInfo}") + } + + def do_-&(that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { + binop(sourceInfo, Interval(this.range -& that.range), SubOp, that) + } + + def do_-%(that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { + throwException(s"Non-growing subtraction is not supported on Intervals: ${sourceInfo}, try squeeze") + } + + final def &(that: Interval): Interval = macro SourceInfoTransform.thatArg + final def |(that: Interval): Interval = macro SourceInfoTransform.thatArg + final def ^(that: Interval): Interval = macro SourceInfoTransform.thatArg + + def do_&(that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = + throwException(s"And is illegal between $this and $that") + def do_|(that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = + throwException(s"Or is illegal between $this and $that") + def do_^(that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = + throwException(s"Xor is illegal between $this and $that") + + final def setPrecision(that: Int): Interval = macro SourceInfoTransform.thatArg + + // Precision change changes range -- see firrtl PrimOps (requires floor) + // aaa.bbb -> aaa.bb for sbp(2) + def do_setPrecision(that: Int)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { + val newBinaryPoint = BinaryPoint(that) + val newIntervalRange = this.range.setPrecision(newBinaryPoint) + binop(sourceInfo, Interval(newIntervalRange), SetBinaryPoint, that) + } + + /** Increase the precision of this Interval, moves the binary point to the left. + * aaa.bbb -> aaa.bbb00 + * @param that how many bits to shift binary point + * @return + */ + final def increasePrecision(that: Int): Interval = macro SourceInfoTransform.thatArg + + def do_increasePrecision(that: Int)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { + assert(that > 0, s"Must increase precision by an integer greater than zero.") + val newBinaryPoint = BinaryPoint(that) + val newIntervalRange = this.range.incPrecision(newBinaryPoint) + binop(sourceInfo, Interval(newIntervalRange), IncreasePrecision, that) + } + + /** Decrease the precision of this Interval, moves the binary point to the right. + * aaa.bbb -> aaa.b + * + * @param that number of bits to move binary point + * @return + */ + final def decreasePrecision(that: Int): Interval = macro SourceInfoTransform.thatArg + + def do_decreasePrecision(that: Int)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { + assert(that > 0, s"Must decrease precision by an integer greater than zero.") + val newBinaryPoint = BinaryPoint(that) + val newIntervalRange = this.range.decPrecision(newBinaryPoint) + binop(sourceInfo, Interval(newIntervalRange), DecreasePrecision, that) + } + + /** Returns this wire bitwise-inverted. */ + def do_unary_~ (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = + throwException(s"Not is illegal on $this") + + override def do_< (that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, LessOp, that) + override def do_> (that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, GreaterOp, that) + override def do_<= (that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, LessEqOp, that) + override def do_>= (that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, GreaterEqOp, that) + + final def != (that: Interval): Bool = macro SourceInfoTransform.thatArg + final def =/= (that: Interval): Bool = macro SourceInfoTransform.thatArg + final def === (that: Interval): Bool = macro SourceInfoTransform.thatArg + + def do_!= (that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, NotEqualOp, that) + def do_=/= (that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, NotEqualOp, that) + def do_=== (that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, EqualOp, that) + + // final def abs(): UInt = macro SourceInfoTransform.noArg + + def do_abs(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { + Mux(this < Interval.Zero, (Interval.Zero - this), this) + } + + override def do_<< (that: Int)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = + binop(sourceInfo, Interval(this.range << that), ShiftLeftOp, that) + + override def do_<< (that: BigInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = + do_<<(that.toInt) + + override def do_<< (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { + binop(sourceInfo, Interval(this.range << that), DynamicShiftLeftOp, that) + } + + override def do_>> (that: Int)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { + binop(sourceInfo, Interval(this.range >> that), ShiftRightOp, that) + } + + override def do_>> (that: BigInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = + do_>>(that.toInt) + + override def do_>> (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { + binop(sourceInfo, Interval(this.range >> that), DynamicShiftRightOp, that) + } + + /** + * Squeeze returns the intersection of the ranges this interval and that Interval + * Ignores binary point of argument + * Treat as an unsafe cast; gives undefined behavior if this signal's value is outside of the resulting range + * Adds no additional hardware; this strictly an unsafe type conversion to use at your own risk + * @param that + * @return + */ + final def squeeze(that: Interval): Interval = macro SourceInfoTransform.thatArg + def do_squeeze(that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { + val other = that + requireIsHardware(this, s"'this' ($this)") + requireIsHardware(other, s"'other' ($other)") + pushOp(DefPrim(sourceInfo, Interval(this.range.squeeze(that.range)), SqueezeOp, this.ref, other.ref)) + } + + /** + * Squeeze returns the intersection of the ranges this interval and that UInt + * Currently, that must have a defined width + * Treat as an unsafe cast; gives undefined behavior if this signal's value is outside of the resulting range + * Adds no additional hardware; this strictly an unsafe type conversion to use at your own risk + * @param that an UInt whose properties determine the squeezing + * @return + */ + final def squeeze(that: UInt): Interval = macro SourceInfoTransform.thatArg + def do_squeeze(that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { + that.widthOption match { + case Some(w) => + do_squeeze(Wire(Interval(IntervalRange(that.width, BinaryPoint(0))))) + case _ => + throwException(s"$this.squeeze($that) requires an UInt argument with a known width") + } + } + + /** + * Squeeze returns the intersection of the ranges this interval and that SInt + * Currently, that must have a defined width + * Treat as an unsafe cast; gives undefined behavior if this signal's value is outside of the resulting range + * Adds no additional hardware; this strictly an unsafe type conversion to use at your own risk + * @param that an SInt whose properties determine the squeezing + * @return + */ + final def squeeze(that: SInt): Interval = macro SourceInfoTransform.thatArg + def do_squeeze(that: SInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { + that.widthOption match { + case Some(w) => + do_squeeze(Wire(Interval(IntervalRange(that.width, BinaryPoint(0))))) + case _ => + throwException(s"$this.squeeze($that) requires an SInt argument with a known width") + } + } + + /** + * Squeeze returns the intersection of the ranges this interval and that IntervalRange + * Ignores binary point of argument + * Treat as an unsafe cast; gives undefined behavior if this signal's value is outside of the resulting range + * Adds no additional hardware; this strictly an unsafe type conversion to use at your own risk + * @param that an Interval whose properties determine the squeezing + * @return + */ + final def squeeze(that: IntervalRange): Interval = macro SourceInfoTransform.thatArg + def do_squeeze(that: IntervalRange)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { + val intervalLitOpt = Interval.getSmallestLegalLit(that) + val intervalLit = intervalLitOpt.getOrElse( + throwException(s"$this.squeeze($that) requires an Interval range with known lower and upper bounds") + ) + do_squeeze(intervalLit) + } + + + /** + * Wrap the value of this [[Interval]] into the range of a different Interval with a presumably smaller range. + * Ignores binary point of argument + * Errors if requires wrapping more than once + * @param that + * @return + */ + final def wrap(that: Interval): Interval = macro SourceInfoTransform.thatArg + + def do_wrap(that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { + val other = that + requireIsHardware(this, s"'this' ($this)") + requireIsHardware(other, s"'other' ($other)") + pushOp(DefPrim(sourceInfo, Interval(this.range.wrap(that.range)), WrapOp, this.ref, other.ref)) + } + + /** + * Wrap this interval into the range determined by that UInt + * Errors if requires wrapping more than once + * @param that an UInt whose properties determine the wrap + * @return + */ + final def wrap(that: UInt): Interval = macro SourceInfoTransform.thatArg + def do_wrap(that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { + that.widthOption match { + case Some(w) => + val u = BigDecimal(BigInt(1) << w) - 1 + do_wrap(0.U.asInterval(IntervalRange(firrtlir.Closed(0), firrtlir.Closed(u), BinaryPoint(0)))) + case _ => + throwException(s"$this.wrap($that) requires UInt with known width") + } + } + + /** + * Wrap this interval into the range determined by an SInt + * Errors if requires wrapping more than once + * @param that an SInt whose properties determine the bounds of the wrap + * @return + */ + final def wrap(that: SInt): Interval = macro SourceInfoTransform.thatArg + def do_wrap(that: SInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { + that.widthOption match { + case Some(w) => + val l = -BigDecimal(BigInt(1) << (that.getWidth - 1)) + val u = BigDecimal(BigInt(1) << (that.getWidth - 1)) - 1 + do_wrap(Wire(Interval(IntervalRange(firrtlir.Closed(l), firrtlir.Closed(u), BinaryPoint(0))))) + case _ => + throwException(s"$this.wrap($that) requires SInt with known width") + } + } + + /** + * Wrap this interval into the range determined by an IntervalRange + * Adds hardware to change values outside of wrapped range to be at the boundary + * Errors if requires wrapping more than once + * Ignores binary point of argument + * @param that an Interval whose properties determine the bounds of the wrap + * @return + */ + final def wrap(that: IntervalRange): Interval = macro SourceInfoTransform.thatArg + def do_wrap(that: IntervalRange)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { + (that.lowerBound, that.upperBound) match { + case (lower: firrtlconstraint.IsKnown, upperBound: firrtlconstraint.IsKnown) => + do_wrap(0.U.asInterval(IntervalRange(that.lowerBound, that.upperBound, BinaryPoint(0)))) + case _ => + throwException(s"$this.wrap($that) requires Interval argument with known lower and upper bounds") + } + } + + /** + * Clip this interval into the range determined by argument's range + * Adds hardware to change values outside of clipped range to be at the boundary + * Ignores binary point of argument + * @param that an Interval whose properties determine the clipping + * @return + */ + final def clip(that: Interval): Interval = macro SourceInfoTransform.thatArg + def do_clip(that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { + binop(sourceInfo, Interval(this.range.clip(that.range)), ClipOp, that) + } + + /** + * Clip this interval into the range determined by argument's range + * Adds hardware to change values outside of clipped range to be at the boundary + * @param that an UInt whose width determines the clipping + * @return + */ + final def clip(that: UInt): Interval = macro SourceInfoTransform.thatArg + def do_clip(that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { + require(that.widthKnown, "UInt clip width must be known") + val u = BigDecimal(BigInt(1) << that.getWidth) - 1 + do_clip(Wire(Interval(IntervalRange(firrtlir.Closed(0), firrtlir.Closed(u), BinaryPoint(0))))) + } + + /** + * Clip this interval into the range determined by argument's range + * Adds hardware to move values outside of clipped range to the boundary + * @param that an SInt whose width determines the clipping + * @return + */ + final def clip(that: SInt): Interval = macro SourceInfoTransform.thatArg + def do_clip(that: SInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { + require(that.widthKnown, "SInt clip width must be known") + val l = -BigDecimal(BigInt(1) << (that.getWidth - 1)) + val u = BigDecimal(BigInt(1) << (that.getWidth - 1)) - 1 + do_clip(Wire(Interval(IntervalRange(firrtlir.Closed(l), firrtlir.Closed(u), BinaryPoint(0))))) + } + + /** + * Clip this interval into the range determined by argument's range + * Adds hardware to move values outside of clipped range to the boundary + * Ignores binary point of argument + * @param that an SInt whose width determines the clipping + * @return + */ + final def clip(that: IntervalRange): Interval = macro SourceInfoTransform.thatArg + def do_clip(that: IntervalRange)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { + (that.lowerBound, that.upperBound) match { + case (lower: firrtlconstraint.IsKnown, upperBound: firrtlconstraint.IsKnown) => + do_clip(0.U.asInterval(IntervalRange(that.lowerBound, that.upperBound, BinaryPoint(0)))) + case _ => + throwException(s"$this.clip($that) requires Interval argument with known lower and upper bounds") + } + } + + override def do_asUInt(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = { + pushOp(DefPrim(sourceInfo, UInt(this.width), AsUIntOp, ref)) + } + override def do_asSInt(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = { + pushOp(DefPrim(sourceInfo, SInt(this.width), AsSIntOp, ref)) + } + + override def do_asFixedPoint(binaryPoint: BinaryPoint)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = { + binaryPoint match { + case KnownBinaryPoint(value) => + val iLit = ILit(value) + pushOp(DefPrim(sourceInfo, FixedPoint(width, binaryPoint), AsFixedPointOp, ref, iLit)) + case _ => + throwException( + s"cannot call $this.asFixedPoint(binaryPoint=$binaryPoint), you must specify a known binaryPoint") + } + } + + // TODO: intervals chick INVALID -- not enough args + def do_asInterval(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { + pushOp(DefPrim(sourceInfo, Interval(this.range), AsIntervalOp, ref)) + throwException(s"($this).asInterval must specify arguments INVALID") + } + + // TODO:(chick) intervals chick looks like this is wrong and only for FP? + def do_fromBits(that: Bits)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): this.type = { + /*val res = Wire(this, null).asInstanceOf[this.type] + res := (that match { + case fp: FixedPoint => fp.asSInt.asFixedPoint(this.binaryPoint) + case _ => that.asFixedPoint(this.binaryPoint) + }) + res*/ + throwException("fromBits INVALID for intervals") + } + + private[chisel3] override def connectFromBits(that: Bits) + (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions) { + this := that.asInterval(this.range) + } + } + + /** Use PrivateObject to force users to specify width and binaryPoint by name + */ + + /** + * Factory and convenience methods for the Interval class + * IMPORTANT: The API provided here is experimental and may change in the future. + */ + object Interval extends NumObject { + /** Create an Interval type with inferred width and binary point. */ + def apply(): Interval = Interval(range"[?,?]") + + /** Create an Interval type with specified width. */ + def apply(binaryPoint: BinaryPoint): Interval = { + val binaryPointString = binaryPoint match { + case KnownBinaryPoint(value) => s"$value" + case _ => s"" + } + Interval(range"[?,?].$binaryPointString") + } + + /** Create an Interval type with specified width. */ + def apply(width: Width): Interval = Interval(width, 0.BP) + + /** Create an Interval type with specified width and binary point */ + def apply(width: Width, binaryPoint: BinaryPoint): Interval = { + Interval(IntervalRange(width, binaryPoint)) + } + + /** Create an Interval type with specified range. + * @param range defines the properties + */ + def apply(range: IntervalRange): Interval = { + new Interval(range) + } + + /** Creates a Interval connected to a Interval literal with the value zero */ + def Zero: Interval = Lit(0, 1.W, 0.BP) + + /** Creates an Interval zero that supports the given range + * Useful for creating a Interval register that has a desired number of bits + * {{{ + * val myRegister = RegInit(Interval.Zero(r"[0,12]") + * }}} + * @param range + * @return + */ + def Zero(range: IntervalRange): Interval = Lit(0, range) + + /** Make an interval from this BigInt, the BigInt is treated as bits + * So lower binaryPoint number of bits will treated as mantissa + * + * @param value + * @param width + * @param binaryPoint + * @return + */ + def fromBigInt(value: BigInt, width: Width = Width(), binaryPoint: BinaryPoint = 0.BP): Interval = { + Interval.Lit(value, Width(), binaryPoint) + } + + /** Create an Interval literal with inferred width from Double. + * Use PrivateObject to force users to specify width and binaryPoint by name + */ + def fromDouble(value: Double, dummy: PrivateType = PrivateObject, + width: Width, binaryPoint: BinaryPoint): Interval = { + fromBigInt( + toBigInt(value, binaryPoint), width = width, binaryPoint = binaryPoint + ) + } + + /** Create an Interval literal with inferred width from Double. + * Use PrivateObject to force users to specify width and binaryPoint by name + */ + def fromBigDecimal(value: Double, dummy: PrivateType = PrivateObject, + width: Width, binaryPoint: BinaryPoint): Interval = { + fromBigInt( + toBigInt(value, binaryPoint), width = width, binaryPoint = binaryPoint + ) + } + + protected[chisel3] def Lit(value: BigInt, width: Width, binaryPoint: BinaryPoint): Interval = { + width match { + case KnownWidth(w) => + if(value >= 0 && value.bitLength >= w || value < 0 && value.bitLength > w) { + throw new ChiselException( + s"Error literal interval value $value is too many bits for specified width $w" + ) + } + case _ => + } + val lit = IntervalLit(value, width, binaryPoint) + val bound = firrtlir.Closed(Interval.toBigDecimal(value, binaryPoint.asInstanceOf[KnownBinaryPoint].value)) + val result = new Interval(IntervalRange(bound, bound, binaryPoint)) + lit.bindLitArg(result) + } + + protected[chisel3] def Lit(value: BigInt, range: IntervalRange): Interval = { + val lit = IntervalLit(value, range.getWidth, range.binaryPoint) + val bigDecimal = BigDecimal(value) / (1 << lit.binaryPoint.get) + val inRange = (range.lowerBound, range.upperBound) match { + case (firrtlir.Closed(l), firrtlir.Closed(u)) => l <= bigDecimal && bigDecimal <= u + case (firrtlir.Closed(l), firrtlir.Open(u)) => l <= bigDecimal && bigDecimal < u + case (firrtlir.Open(l), firrtlir.Closed(u)) => l < bigDecimal && bigDecimal <= u + case (firrtlir.Open(l), firrtlir.Open(u)) => l < bigDecimal && bigDecimal < u + } + if(! inRange) { + throw new ChiselException( + s"Error literal interval value $bigDecimal is not contained in specified range $range" + ) + } + val result = Interval(range) + lit.bindLitArg(result) + } + + /** + * This returns the smallest Interval literal that can legally fit in range, if possible + * If the lower bound or binary point is not known then return None + * + * @param range use to figure low number + * @return + */ + def getSmallestLegalLit(range: IntervalRange): Option[Interval] = { + val bp = range.binaryPoint + range.lowerBound match { + case firrtlir.Closed(lowerBound) => + Some(Interval.Lit(toBigInt(lowerBound.toDouble, bp), width = range.getWidth, bp)) + case firrtlir.Open(lowerBound) => + Some(Interval.Lit(toBigInt(lowerBound.toDouble, bp) + BigInt(1), width = range.getWidth, bp)) + case _ => + None + } + } + + /** + * This returns the largest Interval literal that can legally fit in range, if possible + * If the upper bound or binary point is not known then return None + * + * @param range use to figure low number + * @return + */ + def getLargestLegalLit(range: IntervalRange): Option[Interval] = { + val bp = range.binaryPoint + range.upperBound match { + case firrtlir.Closed(upperBound) => + Some(Interval.Lit(toBigInt(upperBound.toDouble, bp), width = range.getWidth, bp)) + case firrtlir.Open(upperBound) => + Some(Interval.Lit(toBigInt(upperBound.toDouble, bp) - BigInt(1), width = range.getWidth, bp)) + case _ => + None + } + } + + /** Contains the implicit classes used to provide the .I methods to create intervals + * from the standard numberic types. + * {{{ + * val x = 7.I + * val y = 7.5.I(4.BP) + * }}} + */ + object Implicits { + implicit class fromBigIntToLiteralInterval(bigInt: BigInt) { + def I: Interval = { + Interval.Lit(bigInt, width = Width(), 0.BP) + } + + def I(binaryPoint: BinaryPoint): Interval = { + Interval.Lit(bigInt, width = Width(), binaryPoint = binaryPoint) + } + + def I(width: Width, binaryPoint: BinaryPoint): Interval = { + Interval.Lit(bigInt, width, binaryPoint) + } + + def I(range: IntervalRange): Interval = { + Interval.Lit(bigInt, range) + } + } + + implicit class fromIntToLiteralInterval(int: Int) extends fromBigIntToLiteralInterval(int) + implicit class fromLongToLiteralInterval(long: Long) extends fromBigIntToLiteralInterval(long) + + implicit class fromBigDecimalToLiteralInterval(bigDecimal: BigDecimal) { + def I: Interval = { + Interval.Lit(Interval.toBigInt(bigDecimal, 0.BP), width = Width(), 0.BP) + } + + def I(binaryPoint: BinaryPoint): Interval = { + Interval.Lit(Interval.toBigInt(bigDecimal, binaryPoint), width = Width(), binaryPoint = binaryPoint) + } + + def I(width: Width, binaryPoint: BinaryPoint): Interval = { + Interval.Lit(Interval.toBigInt(bigDecimal, binaryPoint), width, binaryPoint) + } + + def I(range: IntervalRange): Interval = { + Interval.Lit(Interval.toBigInt(bigDecimal, range.binaryPoint), range) + } + } + + implicit class fromDoubleToLiteralInterval(double: Double) + extends fromBigDecimalToLiteralInterval(BigDecimal(double)) + } + } +} + + diff --git a/core/src/main/scala/chisel3/BlackBox.scala b/core/src/main/scala/chisel3/BlackBox.scala new file mode 100644 index 00000000..f29962d7 --- /dev/null +++ b/core/src/main/scala/chisel3/BlackBox.scala @@ -0,0 +1,181 @@ +// See LICENSE for license details. + +package chisel3 + +import chisel3.experimental.{BaseModule, Param} +import chisel3.internal.BaseBlackBox +import chisel3.internal.Builder.pushCommand +import chisel3.internal.firrtl._ +import chisel3.internal.throwException +import chisel3.internal.sourceinfo.{SourceInfo, UnlocatableSourceInfo} + +package internal { + + private[chisel3] abstract class BaseBlackBox extends BaseModule + +} + +package experimental { + + /** Parameters for BlackBoxes */ + sealed abstract class Param + case class IntParam(value: BigInt) extends Param + case class DoubleParam(value: Double) extends Param + case class StringParam(value: String) extends Param + /** Unquoted String */ + case class RawParam(value: String) extends Param + + /** Defines a black box, which is a module that can be referenced from within + * Chisel, but is not defined in the emitted Verilog. Useful for connecting + * to RTL modules defined outside Chisel. + * + * A variant of BlackBox, this has a more consistent naming scheme in allowing + * multiple top-level IO and does not drop the top prefix. + * + * @example + * Some design require a differential input clock to clock the all design. + * With the xilinx FPGA for example, a Verilog template named IBUFDS must be + * integrated to use differential input: + * {{{ + * IBUFDS #(.DIFF_TERM("TRUE"), + * .IOSTANDARD("DEFAULT")) ibufds ( + * .IB(ibufds_IB), + * .I(ibufds_I), + * .O(ibufds_O) + * ); + * }}} + * + * To instantiate it, a BlackBox can be used like following: + * {{{ + * import chisel3._ + * import chisel3.experimental._ + * + * // Example with Xilinx differential buffer IBUFDS + * class IBUFDS extends ExtModule(Map("DIFF_TERM" -> "TRUE", // Verilog parameters + * "IOSTANDARD" -> "DEFAULT" + * )) { + * val O = IO(Output(Clock())) + * val I = IO(Input(Clock())) + * val IB = IO(Input(Clock())) + * } + * }}} + * @note The parameters API is experimental and may change + */ + abstract class ExtModule(val params: Map[String, Param] = Map.empty[String, Param]) extends BaseBlackBox { + private[chisel3] override def generateComponent(): Component = { + require(!_closed, "Can't generate module more than once") + _closed = true + + val names = nameIds(classOf[ExtModule]) + + // Name ports based on reflection + for (port <- getModulePorts) { + require(names.contains(port), s"Unable to name port $port in $this") + port.setRef(ModuleIO(this, _namespace.name(names(port)))) + } + + // All suggestions are in, force names to every node. + // While BlackBoxes are not supposed to have an implementation, we still need to call + // _onModuleClose on all nodes (for example, Aggregates use it for recursive naming). + for (id <- getIds) { + id._onModuleClose + } + + val firrtlPorts = getModulePorts map {port => Port(port, port.specifiedDirection)} + val component = DefBlackBox(this, name, firrtlPorts, SpecifiedDirection.Unspecified, params) + _component = Some(component) + component + } + + private[chisel3] def initializeInParent(parentCompileOptions: CompileOptions): Unit = { + implicit val sourceInfo = UnlocatableSourceInfo + + for (x <- getModulePorts) { + pushCommand(DefInvalid(sourceInfo, x.ref)) + } + } + } +} + +/** Defines a black box, which is a module that can be referenced from within + * Chisel, but is not defined in the emitted Verilog. Useful for connecting + * to RTL modules defined outside Chisel. + * + * @example + * Some design require a differential input clock to clock the all design. + * With the xilinx FPGA for example, a Verilog template named IBUFDS must be + * integrated to use differential input: + * {{{ + * IBUFDS #(.DIFF_TERM("TRUE"), + * .IOSTANDARD("DEFAULT")) ibufds ( + * .IB(ibufds_IB), + * .I(ibufds_I), + * .O(ibufds_O) + * ); + * }}} + * + * To instantiate it, a BlackBox can be used like following: + * {{{ + * import chisel3._ + * import chisel3.experimental._ + * + * // Example with Xilinx differential buffer IBUFDS + * class IBUFDS extends BlackBox(Map("DIFF_TERM" -> "TRUE", // Verilog parameters + * "IOSTANDARD" -> "DEFAULT" + * )) { + * val io = IO(new Bundle { + * val O = Output(Clock()) // IO names will be the same + * val I = Input(Clock()) // (without 'io_' in prefix) + * val IB = Input(Clock()) // + * }) + * } + * }}} + * @note The parameters API is experimental and may change + */ +abstract class BlackBox(val params: Map[String, Param] = Map.empty[String, Param])(implicit compileOptions: CompileOptions) extends BaseBlackBox { // scalastyle:ignore line.size.limit + def io: Record + + // Allow access to bindings from the compatibility package + protected def _compatIoPortBound() = portsContains(io) // scalastyle:ignore method.name + + private[chisel3] override def generateComponent(): Component = { + _compatAutoWrapPorts() // pre-IO(...) compatibility hack + + // Restrict IO to just io, clock, and reset + require(io != null, "BlackBox must have io") + require(portsContains(io), "BlackBox must have io wrapped in IO(...)") + require(portsSize == 1, "BlackBox must only have io as IO") + + require(!_closed, "Can't generate module more than once") + _closed = true + + val namedPorts = io.elements.toSeq.reverse // ListMaps are stored in reverse order + + // setRef is not called on the actual io. + // There is a risk of user improperly attempting to connect directly with io + // Long term solution will be to define BlackBox IO differently as part of + // it not descending from the (current) Module + for ((name, port) <- namedPorts) { + port.setRef(ModuleIO(this, _namespace.name(name))) + } + + // We need to call forceName and onModuleClose on all of the sub-elements + // of the io bundle, but NOT on the io bundle itself. + // Doing so would cause the wrong names to be assigned, since their parent + // is now the module itself instead of the io bundle. + for (id <- getIds; if id ne io) { + id._onModuleClose + } + + val firrtlPorts = namedPorts map {namedPort => Port(namedPort._2, namedPort._2.specifiedDirection)} + val component = DefBlackBox(this, name, firrtlPorts, io.specifiedDirection, params) + _component = Some(component) + component + } + + private[chisel3] def initializeInParent(parentCompileOptions: CompileOptions): Unit = { + for ((_, port) <- io.elements) { + pushCommand(DefInvalid(UnlocatableSourceInfo, port.ref)) + } + } +} diff --git a/core/src/main/scala/chisel3/BoolFactory.scala b/core/src/main/scala/chisel3/BoolFactory.scala new file mode 100644 index 00000000..bccd6414 --- /dev/null +++ b/core/src/main/scala/chisel3/BoolFactory.scala @@ -0,0 +1,22 @@ +// See LICENSE for license details. + +package chisel3 + +import chisel3.internal.firrtl.{ULit, Width} + +// scalastyle:off method.name + +trait BoolFactory { + /** Creates an empty Bool. + */ + def apply(): Bool = new Bool() + + /** Creates Bool literal. + */ + protected[chisel3] def Lit(x: Boolean): Bool = { + val result = new Bool() + val lit = ULit(if (x) 1 else 0, Width(1)) + // Ensure we have something capable of generating a name. + lit.bindLitArg(result) + } +} diff --git a/core/src/main/scala/chisel3/Clock.scala b/core/src/main/scala/chisel3/Clock.scala new file mode 100644 index 00000000..d7975b1e --- /dev/null +++ b/core/src/main/scala/chisel3/Clock.scala @@ -0,0 +1,43 @@ +// See LICENSE for license details. + +package chisel3 + +import scala.language.experimental.macros +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(private[chisel3] val width: Width = Width(1)) extends Element { + override def toString: String = s"Clock$bindingToString" + + def cloneType: this.type = Clock().asInstanceOf[this.type] + + private[chisel3] def typeEquivalent(that: Data): Boolean = + this.getClass == that.getClass + + override def connect(that: Data)(implicit sourceInfo: SourceInfo, connectCompileOptions: CompileOptions): Unit = that match { // scalastyle:ignore line.size.limit + case _: Clock => super.connect(that)(sourceInfo, connectCompileOptions) + case _ => super.badConnect(that)(sourceInfo) + } + + override def litOption: Option[BigInt] = None + + /** Not really supported */ + def toPrintable: Printable = PString("CLOCK") + + /** Returns the contents of the clock wire as a [[Bool]]. */ + final def asBool(): Bool = macro SourceInfoTransform.noArg + def do_asBool(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = this.asUInt().asBool() + + override def do_asUInt(implicit sourceInfo: SourceInfo, connectCompileOptions: CompileOptions): UInt = pushOp(DefPrim(sourceInfo, UInt(this.width), AsUIntOp, ref)) // scalastyle:ignore line.size.limit + private[chisel3] override def connectFromBits(that: Bits)(implicit sourceInfo: SourceInfo, + compileOptions: CompileOptions): Unit = { + this := that.asBool.asClock + } +} diff --git a/core/src/main/scala/chisel3/CompileOptions.scala b/core/src/main/scala/chisel3/CompileOptions.scala new file mode 100644 index 00000000..ed410c6e --- /dev/null +++ b/core/src/main/scala/chisel3/CompileOptions.scala @@ -0,0 +1,80 @@ +// See LICENSE for license details. + +package chisel3 + +import scala.language.experimental.macros +import scala.reflect.macros.blackbox.Context + +trait CompileOptions { + // Should Record connections require a strict match of fields. + // If true and the same fields aren't present in both source and sink, a MissingFieldException, + // MissingLeftFieldException, or MissingRightFieldException will be thrown. + val connectFieldsMustMatch: Boolean + // When creating an object that takes a type argument, the argument must be unbound (a pure type). + val declaredTypeMustBeUnbound: Boolean + // If a connection operator fails, don't try the connection with the operands (source and sink) reversed. + val dontTryConnectionsSwapped: Boolean + // If connection directionality is not explicit, do not use heuristics to attempt to determine it. + val dontAssumeDirectionality: Boolean + // Check that referenced Data have actually been declared. + val checkSynthesizable: Boolean + // Require explicit assignment of DontCare to generate "x is invalid" + val explicitInvalidate: Boolean + // Should the reset type of Module be a Bool or a Reset + val inferModuleReset: Boolean +} + +object CompileOptions { + // Provides a low priority Strict default. Can be overridden by importing the NotStrict option. + // Implemented as a macro to prevent this from being used inside chisel core. + implicit def materialize: CompileOptions = macro materialize_impl + + def materialize_impl(c: Context): c.Tree = { + import c.universe._ + q"_root_.chisel3.ExplicitCompileOptions.Strict" + } +} + +object ExplicitCompileOptions { + case class CompileOptionsClass ( + // Should Record connections require a strict match of fields. + // If true and the same fields aren't present in both source and sink, a MissingFieldException, + // MissingLeftFieldException, or MissingRightFieldException will be thrown. + val connectFieldsMustMatch: Boolean, + // When creating an object that takes a type argument, the argument must be unbound (a pure type). + val declaredTypeMustBeUnbound: Boolean, + // If a connection operator fails, don't try the connection with the operands (source and sink) reversed. + val dontTryConnectionsSwapped: Boolean, + // If connection directionality is not explicit, do not use heuristics to attempt to determine it. + val dontAssumeDirectionality: Boolean, + // Check that referenced Data have actually been declared. + val checkSynthesizable: Boolean, + // Require an explicit DontCare assignment to generate a firrtl DefInvalid + val explicitInvalidate: Boolean, + // Should the reset type of Module be a Bool or a Reset + val inferModuleReset: Boolean + ) extends CompileOptions + + // Collection of "not strict" connection compile options. + // These provide compatibility with existing code. + implicit val NotStrict = new CompileOptionsClass ( + connectFieldsMustMatch = false, + declaredTypeMustBeUnbound = false, + dontTryConnectionsSwapped = false, + dontAssumeDirectionality = false, + checkSynthesizable = false, + explicitInvalidate = false, + inferModuleReset = false + ) + + // Collection of "strict" connection compile options, preferred for new code. + implicit val Strict = new CompileOptionsClass ( + connectFieldsMustMatch = true, + declaredTypeMustBeUnbound = true, + dontTryConnectionsSwapped = true, + dontAssumeDirectionality = true, + checkSynthesizable = true, + explicitInvalidate = true, + inferModuleReset = true + ) +} diff --git a/core/src/main/scala/chisel3/Data.scala b/core/src/main/scala/chisel3/Data.scala new file mode 100644 index 00000000..6574a39d --- /dev/null +++ b/core/src/main/scala/chisel3/Data.scala @@ -0,0 +1,729 @@ +// See LICENSE for license details. + +package chisel3 + +import scala.language.experimental.macros +import chisel3.experimental.{Analog, DataMirror, FixedPoint, Interval} +import chisel3.internal.Builder.pushCommand +import chisel3.internal._ +import chisel3.internal.firrtl._ +import chisel3.internal.sourceinfo.{DeprecatedSourceInfo, SourceInfo, SourceInfoTransform, UnlocatableSourceInfo} + +/** User-specified directions. + */ +sealed abstract class SpecifiedDirection +object SpecifiedDirection { + /** Default user direction, also meaning 'not-flipped' + */ + case object Unspecified extends SpecifiedDirection + /** Node and its children are forced as output + */ + case object Output extends SpecifiedDirection + /** Node and its children are forced as inputs + */ + case object Input extends SpecifiedDirection + /** Mainly for containers, children are flipped. + */ + case object Flip extends SpecifiedDirection + + def flip(dir: SpecifiedDirection): SpecifiedDirection = dir match { + case Unspecified => Flip + case Flip => Unspecified + case Output => Input + case Input => Output + } + + /** Returns the effective SpecifiedDirection of this node given the parent's effective SpecifiedDirection + * and the user-specified SpecifiedDirection of this node. + */ + def fromParent(parentDirection: SpecifiedDirection, thisDirection: SpecifiedDirection): SpecifiedDirection = + (parentDirection, thisDirection) match { + case (SpecifiedDirection.Output, _) => SpecifiedDirection.Output + case (SpecifiedDirection.Input, _) => SpecifiedDirection.Input + case (SpecifiedDirection.Unspecified, thisDirection) => thisDirection + case (SpecifiedDirection.Flip, thisDirection) => SpecifiedDirection.flip(thisDirection) + } + + private[chisel3] def specifiedDirection[T<:Data](source: T)(dir: SpecifiedDirection)(implicit compileOptions: CompileOptions): T = { + if (compileOptions.checkSynthesizable) { + requireIsChiselType(source) + } + val out = source.cloneType.asInstanceOf[T] + out.specifiedDirection = dir + out + } + +} + +/** 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 { + /** The object does not exist / is empty and hence has no direction + */ + case object Empty extends 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 + + def fromSpecified(direction: SpecifiedDirection): ActualDirection = direction match { + case SpecifiedDirection.Unspecified | SpecifiedDirection.Flip => ActualDirection.Unspecified + case SpecifiedDirection.Output => ActualDirection.Output + case SpecifiedDirection.Input => ActualDirection.Input + } + + /** Determine the actual binding of a container given directions of its children. + * Returns None in the case of mixed specified / unspecified directionality. + */ + def fromChildren(childDirections: Set[ActualDirection], containerDirection: SpecifiedDirection): + Option[ActualDirection] = { + if (childDirections == Set()) { // Sadly, Scala can't do set matching + ActualDirection.fromSpecified(containerDirection) match { + case ActualDirection.Unspecified => Some(ActualDirection.Empty) // empty direction if relative / no direction + case dir => Some(dir) // use assigned direction if specified + } + } else if (childDirections == Set(ActualDirection.Unspecified)) { + Some(ActualDirection.Unspecified) + } else if (childDirections == Set(ActualDirection.Input)) { + Some(ActualDirection.Input) + } else if (childDirections == Set(ActualDirection.Output)) { + Some(ActualDirection.Output) + } else if (childDirections subsetOf + Set(ActualDirection.Output, ActualDirection.Input, + ActualDirection.Bidirectional(ActualDirection.Default), + ActualDirection.Bidirectional(ActualDirection.Flipped))) { + containerDirection match { + case SpecifiedDirection.Unspecified => Some(ActualDirection.Bidirectional(ActualDirection.Default)) + case SpecifiedDirection.Flip => Some(ActualDirection.Bidirectional(ActualDirection.Flipped)) + case _ => throw new RuntimeException("Unexpected forced Input / Output") + } + } else { + None + } + } +} + +package experimental { + + /** Experimental hardware construction reflection API + */ + object DataMirror { + def widthOf(target: Data): Width = target.width + def specifiedDirectionOf(target: Data): SpecifiedDirection = target.specifiedDirection + def directionOf(target: Data): ActualDirection = { + requireIsHardware(target, "node requested directionality on") + target.direction + } + + // Returns the top-level module ports + // TODO: maybe move to something like Driver or DriverUtils, since this is mainly for interacting + // with compiled artifacts (vs. elaboration-time reflection)? + def modulePorts(target: BaseModule): Seq[(String, Data)] = target.getChiselPorts + + // Returns all module ports with underscore-qualified names + def fullModulePorts(target: BaseModule): Seq[(String, Data)] = { + def getPortNames(name: String, data: Data): Seq[(String, Data)] = Seq(name -> data) ++ (data match { + case _: Element => Seq() + case r: Record => r.elements.toSeq flatMap { case (eltName, elt) => getPortNames(s"${name}_${eltName}", elt) } + case v: Vec[_] => v.zipWithIndex flatMap { case (elt, index) => getPortNames(s"${name}_${index}", elt) } + }) + modulePorts(target).flatMap { case (name, data) => + getPortNames(name, data).toList + } + } + + // Internal reflection-style APIs, subject to change and removal whenever. + object internal { // scalastyle:ignore object.name + def isSynthesizable(target: Data): Boolean = target.isSynthesizable + // For those odd cases where you need to care about object reference and uniqueness + def chiselTypeClone[T<:Data](target: Data): T = { + target.cloneTypeFull.asInstanceOf[T] + } + } + } +} + +/** Creates a clone of the super-type of the input elements. Super-type is defined as: + * - for Bits type of the same class: the cloned type of the largest width + * - Bools are treated as UInts + * - For other types of the same class are are the same: clone of any of the elements + * - Otherwise: fail + */ +//scalastyle:off cyclomatic.complexity +private[chisel3] object cloneSupertype { + def apply[T <: Data](elts: Seq[T], createdType: String)(implicit sourceInfo: SourceInfo, + compileOptions: CompileOptions): T = { + require(!elts.isEmpty, s"can't create $createdType with no inputs") + + val filteredElts = elts.filter(_ != DontCare) + require(!filteredElts.isEmpty, s"can't create $createdType with only DontCare inputs") + + if (filteredElts.head.isInstanceOf[Bits]) { + val model: T = filteredElts reduce { (elt1: T, elt2: T) => ((elt1, elt2) match { + case (elt1: Bool, elt2: Bool) => elt1 + case (elt1: Bool, elt2: UInt) => elt2 // TODO: what happens with zero width UInts? + case (elt1: UInt, elt2: Bool) => elt1 // TODO: what happens with zero width UInts? + case (elt1: UInt, elt2: UInt) => + // TODO: perhaps redefine Widths to allow >= op? + if (elt1.width == (elt1.width max elt2.width)) elt1 else elt2 + case (elt1: SInt, elt2: SInt) => if (elt1.width == (elt1.width max elt2.width)) elt1 else elt2 + case (elt1: FixedPoint, elt2: FixedPoint) => { + (elt1.binaryPoint, elt2.binaryPoint, elt1.width, elt2.width) match { + case (KnownBinaryPoint(bp1), KnownBinaryPoint(bp2), KnownWidth(w1), KnownWidth(w2)) => + val maxBinaryPoint = bp1 max bp2 + val maxIntegerWidth = (w1 - bp1) max (w2 - bp2) + FixedPoint((maxIntegerWidth + maxBinaryPoint).W, (maxBinaryPoint).BP) + case (KnownBinaryPoint(bp1), KnownBinaryPoint(bp2), _, _) => + FixedPoint(Width(), (bp1 max bp2).BP) + case _ => FixedPoint() + } + } + case (elt1: Interval, elt2: Interval) => + val range = if(elt1.range.width == elt1.range.width.max(elt2.range.width)) elt1.range else elt2.range + Interval(range) + case (elt1, elt2) => + throw new AssertionError( + s"can't create $createdType with heterogeneous types ${elt1.getClass} and ${elt2.getClass}") + }).asInstanceOf[T] } + model.cloneTypeFull + } + else { + for (elt <- filteredElts.tail) { + require(elt.getClass == filteredElts.head.getClass, + s"can't create $createdType with heterogeneous types ${filteredElts.head.getClass} and ${elt.getClass}") + require(elt typeEquivalent filteredElts.head, + s"can't create $createdType with non-equivalent types ${filteredElts.head} and ${elt}") + } + filteredElts.head.cloneTypeFull + } + } +} + +/** Returns the chisel type of a hardware object, allowing other hardware to be constructed from it. + */ +object chiselTypeOf { + def apply[T <: Data](target: T): T = { + requireIsHardware(target) + target.cloneTypeFull.asInstanceOf[T] + } +} + +/** +* Input, Output, and Flipped are used to define the directions of Module IOs. +* +* Note that they currently clone their source argument, including its bindings. +* +* 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 = { + SpecifiedDirection.specifiedDirection(source)(SpecifiedDirection.Input) + } +} +object Output { + def apply[T<:Data](source: T)(implicit compileOptions: CompileOptions): T = { + SpecifiedDirection.specifiedDirection(source)(SpecifiedDirection.Output) + } +} + +object Flipped { + def apply[T<:Data](source: T)(implicit compileOptions: CompileOptions): T = { + SpecifiedDirection.specifiedDirection(source)(SpecifiedDirection.flip(source.specifiedDirection)) + } +} + +/** This forms the root of the type system for wire data types. The data value + * must be representable as some number (need not be known at Chisel compile + * time) of bits, and must have methods to pack / unpack structured data to / + * from bits. + * + * @groupdesc Connect Utilities for connecting hardware components + * @define coll data + */ +abstract class Data extends HasId with NamedComponent with SourceInfoDoc { // scalastyle:ignore number.of.methods + // This is a bad API that punches through object boundaries. + @deprecated("pending removal once all instances replaced", "chisel3") + private[chisel3] def flatten: IndexedSeq[Element] = { + this match { + case elt: Aggregate => elt.getElements.toIndexedSeq flatMap {_.flatten} + case elt: Element => IndexedSeq(elt) + case elt => throwException(s"Cannot flatten type ${elt.getClass}") + } + } + + // User-specified direction, local at this node only. + // Note that the actual direction of this node can differ from child and parent specifiedDirection. + private var _specifiedDirection: SpecifiedDirection = SpecifiedDirection.Unspecified + private[chisel3] def specifiedDirection: SpecifiedDirection = _specifiedDirection + private[chisel3] def specifiedDirection_=(direction: SpecifiedDirection) = { + if (_specifiedDirection != SpecifiedDirection.Unspecified) { + this match { + // Anything flies in compatibility mode + case t: Record if !t.compileOptions.dontAssumeDirectionality => + case _ => throw RebindingException(s"Attempted reassignment of user-specified direction to $this") + } + } + _specifiedDirection = direction + } + + /** This overwrites a relative SpecifiedDirection with an explicit one, and is used to implement + * the compatibility layer where, at the elements, Flip is Input and unspecified is Output. + * DO NOT USE OUTSIDE THIS PURPOSE. THIS OPERATION IS DANGEROUS! + */ + private[chisel3] def _assignCompatibilityExplicitDirection: Unit = { // scalastyle:off method.name + (this, _specifiedDirection) match { + case (_: Analog, _) => // nothing to do + case (_, SpecifiedDirection.Unspecified) => _specifiedDirection = SpecifiedDirection.Output + case (_, SpecifiedDirection.Flip) => _specifiedDirection = SpecifiedDirection.Input + case (_, SpecifiedDirection.Input | SpecifiedDirection.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 + // Only valid after node is bound (synthesizable), crashes otherwise + protected[chisel3] def binding: Option[Binding] = _binding + protected def binding_=(target: Binding) { + if (_binding.isDefined) { + throw RebindingException(s"Attempted reassignment of binding to $this") + } + _binding = Some(target) + } + + // Similar to topBindingOpt except it explicitly excludes SampleElements which are bound but not + // hardware + private[chisel3] final def isSynthesizable: Boolean = _binding.map { + case ChildBinding(parent) => parent.isSynthesizable + case _: TopBinding => true + case _: SampleElementBinding[_] => false + }.getOrElse(false) + + private[chisel3] def topBindingOpt: Option[TopBinding] = _binding.flatMap { + case ChildBinding(parent) => parent.topBindingOpt + case bindingVal: TopBinding => Some(bindingVal) + case SampleElementBinding(parent) => parent.topBindingOpt + } + + private[chisel3] def topBinding: TopBinding = topBindingOpt.get + + /** 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: SpecifiedDirection = SpecifiedDirection.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[chisel3] def direction_=(actualDirection: ActualDirection) { + if (_direction.isDefined) { + throw RebindingException(s"Attempted reassignment of resolved direction to $this") + } + _direction = Some(actualDirection) + } + + // User-friendly representation of the binding as a helper function for toString. + // Provides a unhelpful fallback for literals, which should have custom rendering per + // Data-subtype. + // TODO Is this okay for sample_element? It *shouldn't* be visible to users + protected def bindingToString: String = topBindingOpt match { + case None => "" + case Some(OpBinding(enclosure)) => s"(OpResult in ${enclosure.desiredName})" + case Some(MemoryPortBinding(enclosure)) => s"(MemPort in ${enclosure.desiredName})" + case Some(PortBinding(enclosure)) if !enclosure.isClosed => s"(IO in unelaborated ${enclosure.desiredName})" + case Some(PortBinding(enclosure)) if enclosure.isClosed => + DataMirror.fullModulePorts(enclosure).find(_._2 eq this) match { + case Some((name, _)) => s"(IO $name in ${enclosure.desiredName})" + case None => s"(IO (unknown) in ${enclosure.desiredName})" + } + case Some(RegBinding(enclosure)) => s"(Reg in ${enclosure.desiredName})" + case Some(WireBinding(enclosure)) => s"(Wire in ${enclosure.desiredName})" + case Some(DontCareBinding()) => s"(DontCare)" + case Some(ElementLitBinding(litArg)) => s"(unhandled literal)" + case Some(BundleLitBinding(litMap)) => s"(unhandled bundle literal)" + } + + // 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 + private[chisel3] def allElements: Seq[Element] + + private[chisel3] def badConnect(that: Data)(implicit sourceInfo: SourceInfo): Unit = + throwException(s"cannot connect ${this} and ${that}") + private[chisel3] def connect(that: Data)(implicit sourceInfo: SourceInfo, connectCompileOptions: CompileOptions): Unit = { // scalastyle:ignore line.size.limit + if (connectCompileOptions.checkSynthesizable) { + requireIsHardware(this, "data to be connected") + requireIsHardware(that, "data to be connected") + this.topBinding match { + case _: ReadOnlyBinding => throwException(s"Cannot reassign to read-only $this") + case _ => // fine + } + try { + MonoConnect.connect(sourceInfo, connectCompileOptions, this, that, Builder.referenceUserModule) + } catch { + case MonoConnectException(message) => + throwException( + s"Connection between sink ($this) and source ($that) failed @$message" + ) + } + } else { + this legacyConnect that + } + } + private[chisel3] def bulkConnect(that: Data)(implicit sourceInfo: SourceInfo, connectCompileOptions: CompileOptions): Unit = { // scalastyle:ignore line.size.limit + if (connectCompileOptions.checkSynthesizable) { + requireIsHardware(this, s"data to be bulk-connected") + requireIsHardware(that, s"data to be bulk-connected") + (this.topBinding, that.topBinding) match { + case (_: ReadOnlyBinding, _: ReadOnlyBinding) => throwException(s"Both $this and $that are read-only") + // DontCare cannot be a sink (LHS) + case (_: DontCareBinding, _) => throw BiConnect.DontCareCantBeSink + case _ => // fine + } + try { + BiConnect.connect(sourceInfo, connectCompileOptions, this, that, Builder.referenceUserModule) + } catch { + case BiConnectException(message) => + throwException( + s"Connection between left ($this) and source ($that) failed @$message" + ) + } + } else { + this legacyConnect that + } + } + + /** Whether this Data has the same model ("data type") as that Data. + * Data subtypes should overload this with checks against their own type. + */ + private[chisel3] def typeEquivalent(that: Data): Boolean + + // Internal API: returns a ref that can be assigned to, if consistent with the binding + private[chisel3] def lref: Node = { + requireIsHardware(this) + topBindingOpt match { + case Some(binding: ReadOnlyBinding) => throwException(s"internal error: attempted to generate LHS ref to ReadOnlyBinding $binding") // scalastyle:ignore line.size.limit + case Some(binding: TopBinding) => Node(this) + case opt => throwException(s"internal error: unknown binding $opt in generating LHS ref") + } + } + + + // Internal API: returns a ref, if bound. Literals should override this as needed. + private[chisel3] def ref: Arg = { + requireIsHardware(this) + topBindingOpt match { + case Some(binding: LitBinding) => throwException(s"internal error: can't handle literal binding $binding") + case Some(binding: TopBinding) => Node(this) + case opt => throwException(s"internal error: unknown binding $opt in generating LHS ref") + } + } + + private[chisel3] def width: Width + private[chisel3] def legacyConnect(that: Data)(implicit sourceInfo: SourceInfo): Unit + + /** Internal API; Chisel users should look at chisel3.chiselTypeOf(...). + * + * cloneType must be defined for any Chisel object extending Data. + * It is responsible for constructing a basic copy of the object being cloned. + * + * @return a copy of the object. + */ + def cloneType: this.type + + /** Internal API; Chisel users should look at chisel3.chiselTypeOf(...). + * + * Returns a copy of this data type, with hardware bindings (if any) removed. + * Directionality data is still preserved. + */ + private[chisel3] def cloneTypeFull: this.type = { + val clone = this.cloneType.asInstanceOf[this.type] // get a fresh object, without bindings + // Only the top-level direction needs to be fixed up, cloneType should do the rest + clone.specifiedDirection = specifiedDirection + clone + } + + /** Connect this $coll to that $coll mono-directionally and element-wise. + * + * This uses the [[MonoConnect]] algorithm. + * + * @param that the $coll to connect to + * @group Connect + */ + final def := (that: Data)(implicit sourceInfo: SourceInfo, connectionCompileOptions: CompileOptions): Unit = this.connect(that)(sourceInfo, connectionCompileOptions) // scalastyle:ignore line.size.limit + + /** Connect this $coll to that $coll bi-directionally and element-wise. + * + * This uses the [[BiConnect]] algorithm. + * + * @param that the $coll to connect to + * @group Connect + */ + final def <> (that: Data)(implicit sourceInfo: SourceInfo, connectionCompileOptions: CompileOptions): Unit = this.bulkConnect(that)(sourceInfo, connectionCompileOptions) // scalastyle:ignore line.size.limit + + @chiselRuntimeDeprecated + @deprecated("litArg is deprecated, use litOption or litTo*Option", "3.2") + def litArg(): Option[LitArg] = topBindingOpt match { + case Some(ElementLitBinding(litArg)) => Some(litArg) + case Some(BundleLitBinding(litMap)) => None // this API does not support Bundle literals + case _ => None + } + + def isLit(): Boolean = litOption.isDefined + + /** + * If this is a literal that is representable as bits, returns the value as a BigInt. + * If not a literal, or not representable as bits (for example, is or contains Analog), returns None. + */ + def litOption(): Option[BigInt] + + /** + * Returns the literal value if this is a literal that is representable as bits, otherwise crashes. + */ + def litValue(): BigInt = litOption.get + + /** Returns the width, in bits, if currently known. */ + final def getWidth: Int = + if (isWidthKnown) width.get else throwException(s"Width of $this is unknown!") + /** Returns whether the width is currently known. */ + final def isWidthKnown: Boolean = width.known + /** Returns Some(width) if the width is known, else None. */ + final def widthOption: Option[Int] = if (isWidthKnown) Some(getWidth) else None + + /** Does a reinterpret cast of the bits in this node into the format that provides. + * Returns a new Wire of that type. Does not modify existing nodes. + * + * x.asTypeOf(that) performs the inverse operation of x := that.toBits. + * + * @note bit widths are NOT checked, may pad or drop bits from input + * @note that should have known widths + */ + def asTypeOf[T <: Data](that: T): T = macro SourceInfoTransform.thatArg + + /** @group SourceInfoTransformMacro */ + def do_asTypeOf[T <: Data](that: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = { + val thatCloned = Wire(that.cloneTypeFull) + thatCloned.connectFromBits(this.asUInt()) + thatCloned + } + + /** Assigns this node from Bits type. Internal implementation for asTypeOf. + */ + private[chisel3] def connectFromBits(that: Bits)(implicit sourceInfo: SourceInfo, + compileOptions: CompileOptions): Unit + + /** Reinterpret cast to UInt. + * + * @note value not guaranteed to be preserved: for example, a SInt of width + * 3 and value -1 (0b111) would become an UInt with value 7 + * @note Aggregates are recursively packed with the first element appearing + * in the least-significant bits of the result. + */ + final def asUInt(): UInt = macro SourceInfoTransform.noArg + + /** @group SourceInfoTransformMacro */ + def do_asUInt(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt + + /** Default pretty printing */ + def toPrintable: Printable +} + +trait WireFactory { + /** Construct a [[Wire]] from a type template + * @param t The template from which to construct this wire + */ + def apply[T <: Data](t: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = { + if (compileOptions.declaredTypeMustBeUnbound) { + requireIsChiselType(t, "wire type") + } + val x = t.cloneTypeFull + + // Bind each element of x to being a Wire + x.bind(WireBinding(Builder.forcedUserModule)) + + pushCommand(DefWire(sourceInfo, x)) + if (!compileOptions.explicitInvalidate) { + pushCommand(DefInvalid(sourceInfo, x.ref)) + } + + x + } +} + +/** Utility for constructing hardware wires + * + * The width of a `Wire` (inferred or not) is copied from the type template + * {{{ + * val w0 = Wire(UInt()) // width is inferred + * val w1 = Wire(UInt(8.W)) // width is set to 8 + * + * val w2 = Wire(Vec(4, UInt())) // width is inferred + * val w3 = Wire(Vec(4, UInt(8.W))) // width of each element is set to 8 + * + * class MyBundle { + * val unknown = UInt() + * val known = UInt(8.W) + * } + * val w4 = Wire(new MyBundle) + * // Width of w4.unknown is inferred + * // Width of w4.known is set to 8 + * }}} + * + */ +object Wire extends WireFactory + +/** Utility for constructing hardware wires with a default connection + * + * The two forms of `WireDefault` differ in how the type and width of the resulting [[Wire]] are + * specified. + * + * ==Single Argument== + * The single argument form uses the argument to specify both the type and default connection. For + * non-literal [[Bits]], the width of the [[Wire]] will be inferred. For literal [[Bits]] and all + * non-Bits arguments, the type will be copied from the argument. See the following examples for + * more details: + * + * 1. Literal [[Bits]] initializer: width will be set to match + * {{{ + * val w1 = WireDefault(1.U) // width will be inferred to be 1 + * val w2 = WireDefault(1.U(8.W)) // width is set to 8 + * }}} + * + * 2. Non-Literal [[Element]] initializer - width will be inferred + * {{{ + * val x = Wire(UInt()) + * val y = Wire(UInt(8.W)) + * val w1 = WireDefault(x) // width will be inferred + * val w2 = WireDefault(y) // width will be inferred + * }}} + * + * 3. [[Aggregate]] initializer - width will be set to match the aggregate + * + * {{{ + * class MyBundle { + * val unknown = UInt() + * val known = UInt(8.W) + * } + * val w1 = Wire(new MyBundle) + * val w2 = WireDefault(w1) + * // Width of w2.unknown is inferred + * // Width of w2.known is set to 8 + * }}} + * + * ==Double Argument== + * The double argument form allows the type of the [[Wire]] and the default connection to be + * specified independently. + * + * The width inference semantics for `WireDefault` with two arguments match those of [[Wire]]. The + * first argument to `WireDefault` is the type template which defines the width of the `Wire` in + * exactly the same way as the only argument to [[Wire]]. + * + * More explicitly, you can reason about `WireDefault` with multiple arguments as if it were defined + * as: + * {{{ + * def WireDefault[T <: Data](t: T, init: T): T = { + * val x = Wire(t) + * x := init + * x + * } + * }}} + * + * @note The `Default` in `WireDefault` refers to a `default` connection. This is in contrast to + * [[RegInit]] where the `Init` refers to a value on reset. + */ +object WireDefault { + + private def applyImpl[T <: Data](t: T, init: Data)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = { // scalastyle:ignore line.size.limit + implicit val noSourceInfo = UnlocatableSourceInfo + val x = Wire(t) + requireIsHardware(init, "wire initializer") + x := init + x + } + + /** Construct a [[Wire]] with a type template and a [[chisel3.DontCare]] default + * @param t The type template used to construct this [[Wire]] + * @param init The default connection to this [[Wire]], can only be [[DontCare]] + * @note This is really just a specialized form of `apply[T <: Data](t: T, init: T): T` with [[DontCare]] as `init` + */ + def apply[T <: Data](t: T, init: DontCare.type)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = { // scalastyle:ignore line.size.limit + applyImpl(t, init) + } + + /** Construct a [[Wire]] with a type template and a default connection + * @param t The type template used to construct this [[Wire]] + * @param init The hardware value that will serve as the default value + */ + def apply[T <: Data](t: T, init: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = { + applyImpl(t, init) + } + + /** Construct a [[Wire]] with a default connection + * @param init The hardware value that will serve as a type template and default value + */ + def apply[T <: Data](init: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = { + val model = (init match { + // If init is a literal without forced width OR any non-literal, let width be inferred + case init: Bits if !init.litIsForcedWidth.getOrElse(false) => init.cloneTypeWidth(Width()) + case _ => init.cloneTypeFull + }).asInstanceOf[T] + apply(model, init) + } +} + +package internal { + /** RHS (source) for Invalidate API. + * Causes connection logic to emit a DefInvalid when connected to an output port (or wire). + */ + private[chisel3] object InternalDontCare extends Element { + // This object should be initialized before we execute any user code that refers to it, + // otherwise this "Chisel" object will end up on the UserModule's id list. + // We make it private to chisel3 so it has to be accessed through the package object. + + private[chisel3] override val width: Width = UnknownWidth() + + bind(DontCareBinding(), SpecifiedDirection.Output) + override def cloneType: this.type = DontCare + + override def toString: String = "DontCare()" + + override def litOption: Option[BigInt] = None + + def toPrintable: Printable = PString("DONTCARE") + + private[chisel3] def connectFromBits(that: Bits)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Unit = { // scalastyle:ignore line.size.limit + Builder.error("connectFromBits: DontCare cannot be a connection sink (LHS)") + } + + def do_asUInt(implicit sourceInfo: chisel3.internal.sourceinfo.SourceInfo, compileOptions: CompileOptions): UInt = { // scalastyle:ignore line.size.limit + Builder.error("DontCare does not have a UInt representation") + 0.U + } + // DontCare's only match themselves. + private[chisel3] def typeEquivalent(that: Data): Boolean = that == DontCare + } +} diff --git a/core/src/main/scala/chisel3/Element.scala b/core/src/main/scala/chisel3/Element.scala new file mode 100644 index 00000000..fccae9ab --- /dev/null +++ b/core/src/main/scala/chisel3/Element.scala @@ -0,0 +1,62 @@ +// See LICENSE for license details. + +package chisel3 + +import chisel3.internal.Builder.pushCommand +import chisel3.internal.firrtl._ +import chisel3.internal.sourceinfo.SourceInfo +import chisel3.internal._ + +/** Element is a leaf data type: it cannot contain other [[Data]] objects. Example uses are for representing primitive + * data types, like integers and bits. + * + * @define coll element + */ +abstract class Element extends Data { + private[chisel3] final def allElements: Seq[Element] = Seq(this) + def widthKnown: Boolean = width.known + def name: String = getRef.name + + private[chisel3] override def bind(target: Binding, parentDirection: SpecifiedDirection) { + binding = target + val resolvedDirection = SpecifiedDirection.fromParent(parentDirection, specifiedDirection) + direction = ActualDirection.fromSpecified(resolvedDirection) + } + + private[chisel3] override def topBindingOpt: Option[TopBinding] = super.topBindingOpt match { + // Translate Bundle lit bindings to Element lit bindings + case Some(BundleLitBinding(litMap)) => litMap.get(this) match { + case Some(litArg) => Some(ElementLitBinding(litArg)) + case _ => Some(DontCareBinding()) + } + case topBindingOpt => topBindingOpt + } + + private[chisel3] def litArgOption: Option[LitArg] = topBindingOpt match { + case Some(ElementLitBinding(litArg)) => Some(litArg) + case _ => None + } + + override def litOption: Option[BigInt] = litArgOption.map(_.num) + private[chisel3] def litIsForcedWidth: Option[Boolean] = litArgOption.map(_.forcedWidth) + + // provide bits-specific literal handling functionality here + override private[chisel3] def ref: Arg = topBindingOpt match { + case Some(ElementLitBinding(litArg)) => litArg + case Some(BundleLitBinding(litMap)) => litMap.get(this) match { + case Some(litArg) => litArg + case _ => throwException(s"internal error: DontCare should be caught before getting ref") + } + case _ => super.ref + } + + private[chisel3] def legacyConnect(that: Data)(implicit sourceInfo: SourceInfo): Unit = { + // If the source is a DontCare, generate a DefInvalid for the sink, + // otherwise, issue a Connect. + if (that == DontCare) { + pushCommand(DefInvalid(sourceInfo, Node(this))) + } else { + pushCommand(Connect(sourceInfo, Node(this), that.ref)) + } + } +} diff --git a/core/src/main/scala/chisel3/Mem.scala b/core/src/main/scala/chisel3/Mem.scala new file mode 100644 index 00000000..24ab4b8e --- /dev/null +++ b/core/src/main/scala/chisel3/Mem.scala @@ -0,0 +1,216 @@ +// See LICENSE for license details. + +package chisel3 + +import scala.language.experimental.macros + +import firrtl.{ir => fir} + +import chisel3.internal._ +import chisel3.internal.Builder.pushCommand +import chisel3.internal.firrtl._ +import chisel3.internal.sourceinfo.{SourceInfo, SourceInfoTransform, UnlocatableSourceInfo, MemTransform} + + +object Mem { + + /** Creates a combinational/asynchronous-read, sequential/synchronous-write [[Mem]]. + * + * @param size number of elements in the memory + * @param t data type of memory element + */ + def apply[T <: Data](size: BigInt, t: T): Mem[T] = macro MemTransform.apply[T] + + /** Creates a combinational/asynchronous-read, sequential/synchronous-write [[Mem]]. + * + * @param size number of elements in the memory + * @param t data type of memory element + */ + def apply[T <: Data](size: Int, t: T): Mem[T] = macro MemTransform.apply[T] + + /** @group SourceInfoTransformMacro */ + def do_apply[T <: Data](size: BigInt, t: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Mem[T] = { + if (compileOptions.declaredTypeMustBeUnbound) { + requireIsChiselType(t, "memory type") + } + val mt = t.cloneTypeFull + val mem = new Mem(mt, size) + pushCommand(DefMemory(sourceInfo, mem, mt, size)) + mem + } + + /** @group SourceInfoTransformMacro */ + def do_apply[T <: Data](size: Int, t: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Mem[T] = + do_apply(BigInt(size), t)(sourceInfo, compileOptions) +} + +sealed abstract class MemBase[T <: Data](val t: T, val length: BigInt) extends HasId with NamedComponent with SourceInfoDoc { + // REVIEW TODO: make accessors (static/dynamic, read/write) combinations consistent. + + /** Creates a read accessor into the memory with static addressing. See the + * class documentation of the memory for more detailed information. + */ + def apply(x: BigInt): T = macro SourceInfoTransform.xArg + + /** Creates a read accessor into the memory with static addressing. See the + * class documentation of the memory for more detailed information. + */ + def apply(x: Int): T = macro SourceInfoTransform.xArg + + /** @group SourceInfoTransformMacro */ + def do_apply(idx: BigInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = { + require(idx >= 0 && idx < length) + apply(idx.asUInt) + } + + /** @group SourceInfoTransformMacro */ + def do_apply(idx: Int)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = + do_apply(BigInt(idx))(sourceInfo, compileOptions) + + /** Creates a read/write accessor into the memory with dynamic addressing. + * See the class documentation of the memory for more detailed information. + */ + def apply(x: UInt): T = macro SourceInfoTransform.xArg + + /** @group SourceInfoTransformMacro */ + def do_apply(idx: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = + makePort(sourceInfo, idx, MemPortDirection.INFER) + + /** Creates a read accessor into the memory with dynamic addressing. See the + * class documentation of the memory for more detailed information. + */ + def read(x: UInt): T = macro SourceInfoTransform.xArg + + /** @group SourceInfoTransformMacro */ + def do_read(idx: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = + makePort(sourceInfo, idx, MemPortDirection.READ) + + /** Creates a write accessor into the memory. + * + * @param idx memory element index to write into + * @param data new data to write + */ + def write(idx: UInt, data: T)(implicit compileOptions: CompileOptions): Unit = { + implicit val sourceInfo = UnlocatableSourceInfo + makePort(UnlocatableSourceInfo, idx, MemPortDirection.WRITE) := data + } + + /** Creates a masked write accessor into the memory. + * + * @param idx memory element index to write into + * @param data new data to write + * @param mask write mask as a Seq of Bool: a write to the Vec element in + * memory is only performed if the corresponding mask index is true. + * + * @note this is only allowed if the memory's element data type is a Vec + */ + def write(idx: UInt, data: T, mask: Seq[Bool]) (implicit evidence: T <:< Vec[_], compileOptions: CompileOptions): Unit = { + implicit val sourceInfo = UnlocatableSourceInfo + val accessor = makePort(sourceInfo, idx, MemPortDirection.WRITE).asInstanceOf[Vec[Data]] + val dataVec = data.asInstanceOf[Vec[Data]] + if (accessor.length != dataVec.length) { + Builder.error(s"Mem write data must contain ${accessor.length} elements (found ${dataVec.length})") + } + if (accessor.length != mask.length) { + Builder.error(s"Mem write mask must contain ${accessor.length} elements (found ${mask.length})") + } + for (((cond, port), datum) <- mask zip accessor zip dataVec) + when (cond) { port := datum } + } + + private def makePort(sourceInfo: SourceInfo, idx: UInt, dir: MemPortDirection)(implicit compileOptions: CompileOptions): T = { + requireIsHardware(idx, "memory port index") + val i = Vec.truncateIndex(idx, length)(sourceInfo, compileOptions) + + val port = pushCommand( + DefMemPort(sourceInfo, + t.cloneTypeFull, Node(this), dir, i.ref, Builder.forcedClock.ref) + ).id + // Bind each element of port to being a MemoryPort + port.bind(MemoryPortBinding(Builder.forcedUserModule)) + port + } +} + +/** A combinational/asynchronous-read, sequential/synchronous-write memory. + * + * Writes take effect on the rising clock edge after the request. Reads are + * combinational (requests will return data on the same cycle). + * Read-after-write hazards are not an issue. + * + * @note when multiple conflicting writes are performed on a Mem element, the + * result is undefined (unlike Vec, where the last assignment wins) + */ +sealed class Mem[T <: Data] private (t: T, length: BigInt) extends MemBase(t, length) + +object SyncReadMem { + + + type ReadUnderWrite = fir.ReadUnderWrite.Value + val Undefined = fir.ReadUnderWrite.Undefined + val ReadFirst = fir.ReadUnderWrite.Old + val WriteFirst = fir.ReadUnderWrite.New + + /** Creates a sequential/synchronous-read, sequential/synchronous-write [[SyncReadMem]]. + * + * @param size number of elements in the memory + * @param t data type of memory element + */ + def apply[T <: Data](size: BigInt, t: T): SyncReadMem[T] = macro MemTransform.apply[T] + def apply[T <: Data](size: BigInt, t: T, ruw: ReadUnderWrite): SyncReadMem[T] = macro MemTransform.apply_ruw[T] + + /** Creates a sequential/synchronous-read, sequential/synchronous-write [[SyncReadMem]]. + * + * @param size number of elements in the memory + * @param t data type of memory element + */ + def apply[T <: Data](size: Int, t: T): SyncReadMem[T] = macro MemTransform.apply[T] + def apply[T <: Data](size: Int, t: T, ruw: ReadUnderWrite): SyncReadMem[T] = macro MemTransform.apply_ruw[T] + + /** @group SourceInfoTransformMacro */ + def do_apply[T <: Data](size: BigInt, t: T, ruw: ReadUnderWrite = Undefined)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SyncReadMem[T] = { + if (compileOptions.declaredTypeMustBeUnbound) { + requireIsChiselType(t, "memory type") + } + val mt = t.cloneTypeFull + val mem = new SyncReadMem(mt, size, ruw) + pushCommand(DefSeqMemory(sourceInfo, mem, mt, size, ruw)) + mem + } + + /** @group SourceInfoTransformMacro */ + // Alternate signatures can't use default parameter values + def do_apply[T <: Data](size: Int, t: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SyncReadMem[T] = + do_apply(BigInt(size), t)(sourceInfo, compileOptions) + + /** @group SourceInfoTransformMacro */ + // Alternate signatures can't use default parameter values + def do_apply[T <: Data](size: Int, t: T, ruw: ReadUnderWrite)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SyncReadMem[T] = + do_apply(BigInt(size), t, ruw)(sourceInfo, compileOptions) +} + +/** A sequential/synchronous-read, sequential/synchronous-write memory. + * + * Writes take effect on the rising clock edge after the request. Reads return + * data on the rising edge after the request. Read-after-write behavior (when + * a read and write to the same address are requested on the same cycle) is + * undefined. + * + * @note when multiple conflicting writes are performed on a Mem element, the + * result is undefined (unlike Vec, where the last assignment wins) + */ +sealed class SyncReadMem[T <: Data] private (t: T, n: BigInt, val readUnderWrite: SyncReadMem.ReadUnderWrite) extends MemBase[T](t, n) { + def read(x: UInt, en: Bool): T = macro SourceInfoTransform.xEnArg + + /** @group SourceInfoTransformMacro */ + def do_read(addr: UInt, enable: Bool)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = { + val a = Wire(UInt()) + a := DontCare + var port: Option[T] = None + when (enable) { + a := addr + port = Some(read(a)) + } + port.get + } +} diff --git a/core/src/main/scala/chisel3/Module.scala b/core/src/main/scala/chisel3/Module.scala new file mode 100644 index 00000000..f1c4e30a --- /dev/null +++ b/core/src/main/scala/chisel3/Module.scala @@ -0,0 +1,405 @@ +// See LICENSE for license details. + +package chisel3 + +import scala.collection.immutable.ListMap +import scala.collection.mutable.{ArrayBuffer, HashMap} +import scala.collection.JavaConversions._ +import scala.language.experimental.macros + +import java.util.IdentityHashMap + +import chisel3.internal._ +import chisel3.internal.Builder._ +import chisel3.internal.firrtl._ +import chisel3.internal.sourceinfo.{InstTransform, SourceInfo} +import chisel3.experimental.BaseModule +import _root_.firrtl.annotations.{ModuleName, ModuleTarget, IsModule} + +object Module extends SourceInfoDoc { + /** A wrapper method that all Module instantiations must be wrapped in + * (necessary to help Chisel track internal state). + * + * @param bc the Module being created + * + * @return the input module `m` with Chisel metadata properly set + */ + def apply[T <: BaseModule](bc: => T): T = macro InstTransform.apply[T] + + /** @group SourceInfoTransformMacro */ + def do_apply[T <: BaseModule](bc: => T) + (implicit sourceInfo: SourceInfo, + compileOptions: CompileOptions): T = { + if (Builder.readyForModuleConstr) { + throwException("Error: Called Module() twice without instantiating a Module." + + sourceInfo.makeMessage(" See " + _)) + } + Builder.readyForModuleConstr = true + + val parent = Builder.currentModule + val whenDepth: Int = Builder.whenDepth + + // Save then clear clock and reset to prevent leaking scope, must be set again in the Module + val (saveClock, saveReset) = (Builder.currentClock, Builder.currentReset) + Builder.currentClock = None + Builder.currentReset = None + + // Execute the module, this has the following side effects: + // - set currentModule + // - unset readyForModuleConstr + // - reset whenDepth to 0 + // - set currentClockAndReset + val module: T = bc // bc is actually evaluated here + + if (Builder.whenDepth != 0) { + throwException("Internal Error! when() scope depth is != 0, this should have been caught!") + } + if (Builder.readyForModuleConstr) { + throwException("Error: attempted to instantiate a Module, but nothing happened. " + + "This is probably due to rewrapping a Module instance with Module()." + + sourceInfo.makeMessage(" See " + _)) + } + Builder.currentModule = parent // Back to parent! + Builder.whenDepth = whenDepth + Builder.currentClock = saveClock // Back to clock and reset scope + Builder.currentReset = saveReset + + val component = module.generateComponent() + Builder.components += component + + // Handle connections at enclosing scope + if(!Builder.currentModule.isEmpty) { + pushCommand(DefInstance(sourceInfo, module, component.ports)) + module.initializeInParent(compileOptions) + } + module + } + + /** Returns the implicit Clock */ + def clock: Clock = Builder.forcedClock + /** Returns the implicit Reset */ + def reset: Reset = Builder.forcedReset + /** Returns the current Module */ + def currentModule: Option[BaseModule] = Builder.currentModule +} + +package experimental { + + object IO { + /** Constructs a port for the current Module + * + * This must wrap the datatype used to set the io field of any Module. + * i.e. All concrete modules must have defined io in this form: + * [lazy] val io[: io type] = IO(...[: io type]) + * + * Items in [] are optional. + * + * The granted iodef must be a chisel type and not be bound to hardware. + * + * Also registers a Data as a port, also performing bindings. Cannot be called once ports are + * requested (so that all calls to ports will return the same information). + * Internal API. + */ + def apply[T<:Data](iodef: T): T = { + val module = Module.currentModule.get // Impossible to fail + require(!module.isClosed, "Can't add more ports after module close") + requireIsChiselType(iodef, "io type") + + // Clone the IO so we preserve immutability of data types + val iodefClone = try { + iodef.cloneTypeFull + } catch { + // For now this is going to be just a deprecation so we don't suddenly break everyone's code + case e: AutoClonetypeException => + Builder.deprecated(e.getMessage, Some(s"${iodef.getClass}")) + iodef + } + module.bindIoInPlace(iodefClone) + iodefClone + } + } +} + +package internal { + import chisel3.experimental.BaseModule + + object BaseModule { + private[chisel3] class ClonePorts (elts: Data*)(implicit compileOptions: CompileOptions) extends Record { + val elements = ListMap(elts.map(d => d.instanceName -> d.cloneTypeFull): _*) + def apply(field: String) = elements(field) + override def cloneType = (new ClonePorts(elts: _*)).asInstanceOf[this.type] + } + + private[chisel3] def cloneIORecord(proto: BaseModule)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): ClonePorts = { + require(proto.isClosed, "Can't clone a module before module close") + val clonePorts = new ClonePorts(proto.getModulePorts: _*) + clonePorts.bind(WireBinding(Builder.forcedUserModule)) + val cloneInstance = new DefInstance(sourceInfo, proto, proto._component.get.ports) { + override def name = clonePorts.getRef.name + } + pushCommand(cloneInstance) + if (!compileOptions.explicitInvalidate) { + pushCommand(DefInvalid(sourceInfo, clonePorts.ref)) + } + if (proto.isInstanceOf[MultiIOModule]) { + clonePorts("clock") := Module.clock + clonePorts("reset") := Module.reset + } + clonePorts + } + } +} + +package experimental { + + /** Abstract base class for Modules, an instantiable organizational unit for RTL. + */ + // TODO: seal this? + abstract class BaseModule extends HasId { + // + // Builder Internals - this tracks which Module RTL construction belongs to. + // + if (!Builder.readyForModuleConstr) { + throwException("Error: attempted to instantiate a Module without wrapping it in Module().") + } + readyForModuleConstr = false + + Builder.currentModule = Some(this) + Builder.whenDepth = 0 + + // + // Module Construction Internals + // + protected var _closed = false + + /** Internal check if a Module is closed */ + private[chisel3] def isClosed = _closed + + // Fresh Namespace because in Firrtl, Modules namespaces are disjoint with the global namespace + private[chisel3] val _namespace = Namespace.empty + private val _ids = ArrayBuffer[HasId]() + private[chisel3] def addId(d: HasId) { + if (Builder.aspectModule(this).isDefined) { + aspectModule(this).get.addId(d) + } else { + require(!_closed, "Can't write to module after module close") + _ids += d + } + } + + protected def getIds = { + require(_closed, "Can't get ids before module close") + _ids.toSeq + } + + private val _ports = new ArrayBuffer[Data]() + + // getPorts unfortunately already used for tester compatibility + protected[chisel3] def getModulePorts = { + require(_closed, "Can't get ports before module close") + _ports.toSeq + } + + // These methods allow checking some properties of ports before the module is closed, + // mainly for compatibility purposes. + protected def portsContains(elem: Data): Boolean = _ports contains elem + + protected def portsSize: Int = _ports.size + + /** Generates the FIRRTL Component (Module or Blackbox) of this Module. + * Also closes the module so no more construction can happen inside. + */ + private[chisel3] def generateComponent(): Component + + /** Sets up this module in the parent context + */ + private[chisel3] def initializeInParent(parentCompileOptions: CompileOptions): Unit + + // + // Chisel Internals + // + + /** The desired name of this module (which will be used in generated FIRRTL IR or Verilog). + * + * The name of a module approximates the behavior of the Java Reflection [[`getSimpleName` method + * https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html#getSimpleName--]] with some modifications: + * + * - Anonymous modules will get an `"_Anon"` tag + * - Modules defined in functions will use their class name and not a numeric name + * + * @note If you want a custom or parametric name, override this method. + */ + def desiredName: String = { + /* The default module name is derived from the Java reflection derived class name. */ + val baseName = this.getClass.getName + + /* A sequence of string filters applied to the name */ + val filters: Seq[String => String] = Seq( + ((a: String) => raw"\$$+anon".r.replaceAllIn(a, "_Anon")) // Merge the "$$anon" name with previous name + ) + + filters + .foldLeft(baseName){ case (str, filter) => filter(str) } // 1. Apply filters to baseName + .split("\\.|\\$") // 2. Split string at '.' or '$' + .filterNot(_.forall(_.isDigit)) // 3. Drop purely numeric names + .last // 4. Use the last name + } + + /** Legalized name of this module. */ + final lazy val name = try { + Builder.globalNamespace.name(desiredName) + } catch { + case e: NullPointerException => throwException( + s"Error: desiredName of ${this.getClass.getName} is null. Did you evaluate 'name' before all values needed by desiredName were available?", e) // scalastyle:ignore line.size.limit + case t: Throwable => throw t + } + + /** Returns a FIRRTL ModuleName that references this object + * + * @note Should not be called until circuit elaboration is complete + */ + final def toNamed: ModuleName = toTarget.toNamed + + /** Returns a FIRRTL ModuleTarget that references this object + * + * @note Should not be called until circuit elaboration is complete + */ + final def toTarget: ModuleTarget = ModuleTarget(this.circuitName, this.name) + + /** Returns a FIRRTL ModuleTarget that references this object + * + * @note Should not be called until circuit elaboration is complete + */ + final def toAbsoluteTarget: IsModule = { + _parent match { + case Some(parent) => parent.toAbsoluteTarget.instOf(this.instanceName, toTarget.module) + case None => toTarget + } + } + + /** + * Internal API. Returns a list of this module's generated top-level ports as a map of a String + * (FIRRTL name) to the IO object. Only valid after the module is closed. + * + * Note: for BlackBoxes (but not ExtModules), this returns the contents of the top-level io + * object, consistent with what is emitted in FIRRTL. + * + * TODO: Use SeqMap/VectorMap when those data structures become available. + */ + private[chisel3] def getChiselPorts: Seq[(String, Data)] = { + require(_closed, "Can't get ports before module close") + _component.get.ports.map { port => + (port.id.getRef.asInstanceOf[ModuleIO].name, port.id) + } + } + + /** Called at the Module.apply(...) level after this Module has finished elaborating. + * Returns a map of nodes -> names, for named nodes. + * + * Helper method. + */ + protected def nameIds(rootClass: Class[_]): HashMap[HasId, String] = { + val names = new HashMap[HasId, String]() + + def name(node: HasId, name: String) { + // First name takes priority, like suggestName + // TODO: DRYify with suggestName + if (!names.contains(node)) { + names.put(node, name) + } + } + + /** Scala generates names like chisel3$util$Queue$$ram for private vals + * This extracts the part after $$ for names like this and leaves names + * without $$ unchanged + */ + def cleanName(name: String): String = name.split("""\$\$""").lastOption.getOrElse(name) + + for (m <- getPublicFields(rootClass)) { + Builder.nameRecursively(cleanName(m.getName), m.invoke(this), name) + } + + names + } + + /** Compatibility function. Allows Chisel2 code which had ports without the IO wrapper to + * compile under Bindings checks. Does nothing in non-compatibility mode. + * + * Should NOT be used elsewhere. This API will NOT last. + * + * TODO: remove this, perhaps by removing Bindings checks in compatibility mode. + */ + def _compatAutoWrapPorts() {} // scalastyle:ignore method.name + + /** Chisel2 code didn't require the IO(...) wrapper and would assign a Chisel type directly to + * io, then do operations on it. This binds a Chisel type in-place (mutably) as an IO. + */ + protected def _bindIoInPlace(iodef: Data): Unit = { // scalastyle:ignore method.name + // 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.specifiedDirection match { + // Recurse into children to ensure explicit direction set somewhere + case SpecifiedDirection.Unspecified | SpecifiedDirection.Flip => data match { + case record: Record => + val compatRecord = !record.compileOptions.dontAssumeDirectionality + record.getElements.foreach(assignCompatDir(_, compatRecord)) + case vec: Vec[_] => + vec.getElements.foreach(assignCompatDir(_, insideCompat)) + } + case SpecifiedDirection.Input | SpecifiedDirection.Output => // forced assign, nothing to do + } + } + } + + assignCompatDir(iodef, false) + + iodef.bind(PortBinding(this)) + _ports += iodef + } + + /** Private accessor for _bindIoInPlace */ + private[chisel3] def bindIoInPlace(iodef: Data): Unit = _bindIoInPlace(iodef) + + /** + * This must wrap the datatype used to set the io field of any Module. + * i.e. All concrete modules must have defined io in this form: + * [lazy] val io[: io type] = IO(...[: io type]) + * + * Items in [] are optional. + * + * The granted iodef must be a chisel type and not be bound to hardware. + * + * Also registers a Data as a port, also performing bindings. Cannot be called once ports are + * requested (so that all calls to ports will return the same information). + * Internal API. + * + * TODO(twigg): Specifically walk the Data definition to call out which nodes + * are problematic. + */ + protected def IO[T <: Data](iodef: T): T = chisel3.experimental.IO.apply(iodef) // scalastyle:ignore method.name + + // + // Internal Functions + // + + /** Keep component for signal names */ + private[chisel3] var _component: Option[Component] = None + + /** Signal name (for simulation). */ + override def instanceName: String = + if (_parent == None) name else _component match { + case None => getRef.name + case Some(c) => getRef fullName c + } + + } +} diff --git a/core/src/main/scala/chisel3/ModuleAspect.scala b/core/src/main/scala/chisel3/ModuleAspect.scala new file mode 100644 index 00000000..20793cd7 --- /dev/null +++ b/core/src/main/scala/chisel3/ModuleAspect.scala @@ -0,0 +1,26 @@ +// See LICENSE for license details. + +package chisel3 + +import chisel3.internal.Builder + +/** Used by Chisel Aspects to inject Chisel code into modules, after they have been elaborated. + * This is an internal API - don't use! + * + * It adds itself as an aspect to the module, which allows proper checking of connection and binding legality. + * + * @param module Module for which this object is an aspect of + * @param moduleCompileOptions + */ +abstract class ModuleAspect private[chisel3] (module: RawModule) + (implicit moduleCompileOptions: CompileOptions) extends RawModule { + + Builder.addAspect(module, this) + + override def circuitName: String = module.toTarget.circuit + + override def desiredName: String = module.name + + override val _namespace = module._namespace +} + diff --git a/core/src/main/scala/chisel3/MultiClock.scala b/core/src/main/scala/chisel3/MultiClock.scala new file mode 100644 index 00000000..239e745a --- /dev/null +++ b/core/src/main/scala/chisel3/MultiClock.scala @@ -0,0 +1,70 @@ +// See LICENSE for license details. + +package chisel3 + +import chisel3.internal._ + +import scala.language.experimental.macros + +object withClockAndReset { // scalastyle:ignore object.name + /** Creates a new Clock and Reset scope + * + * @param clock the new implicit Clock + * @param reset the new implicit Reset + * @param block the block of code to run with new implicit Clock and Reset + * @return the result of the block + */ + def apply[T](clock: Clock, reset: Reset)(block: => T): T = { + // Save parentScope + val parentClock = Builder.currentClock + val parentReset = Builder.currentReset + + Builder.currentClock = Some(clock) + Builder.currentReset = Some(reset) + + val res = block // execute block + + // Return to old scope + Builder.currentClock = parentClock + Builder.currentReset = parentReset + res + } +} + +object withClock { // scalastyle:ignore object.name + /** Creates a new Clock scope + * + * @param clock the new implicit Clock + * @param block the block of code to run with new implicit Clock + * @return the result of the block + */ + def apply[T](clock: Clock)(block: => T): T = { + // Save parentScope + val parentClock = Builder.currentClock + Builder.currentClock = Some(clock) + val res = block // execute block + // Return to old scope + Builder.currentClock = parentClock + res + } +} + +object withReset { // scalastyle:ignore object.name + /** Creates a new Reset scope + * + * @param reset the new implicit Reset + * @param block the block of code to run with new implicit Reset + * @return the result of the block + */ + def apply[T](reset: Reset)(block: => T): T = { + // Save parentScope + val parentReset = Builder.currentReset + Builder.currentReset = Some(reset) + val res = block // execute block + // Return to old scope + Builder.currentReset = parentReset + res + } + +} + diff --git a/core/src/main/scala/chisel3/Mux.scala b/core/src/main/scala/chisel3/Mux.scala new file mode 100644 index 00000000..960424bf --- /dev/null +++ b/core/src/main/scala/chisel3/Mux.scala @@ -0,0 +1,50 @@ +// See LICENSE for license details. + +package chisel3 + +import scala.language.experimental.macros + +import chisel3.internal._ +import chisel3.internal.Builder.pushOp +import chisel3.internal.sourceinfo.{SourceInfo, MuxTransform} +import chisel3.internal.firrtl._ +import chisel3.internal.firrtl.PrimOp._ + +object Mux extends SourceInfoDoc { + /** Creates a mux, whose output is one of the inputs depending on the + * value of the condition. + * + * @param cond condition determining the input to choose + * @param con the value chosen when `cond` is true + * @param alt the value chosen when `cond` is false + * @example + * {{{ + * val muxOut = Mux(data_in === 3.U, 3.U(4.W), 0.U(4.W)) + * }}} + */ + def apply[T <: Data](cond: Bool, con: T, alt: T): T = macro MuxTransform.apply[T] + + /** @group SourceInfoTransformMacro */ + def do_apply[T <: Data](cond: Bool, con: T, alt: T)(implicit sourceInfo: SourceInfo, + compileOptions: CompileOptions): T = { + requireIsHardware(cond, "mux condition") + requireIsHardware(con, "mux true value") + requireIsHardware(alt, "mux false value") + val d = cloneSupertype(Seq(con, alt), "Mux") + val conRef = con match { // this matches chisel semantics (DontCare as object) to firrtl semantics (invalidate) + case DontCare => + val dcWire = Wire(d) + dcWire := DontCare + dcWire.ref + case _ => con.ref + } + val altRef = alt match { + case DontCare => + val dcWire = Wire(d) + dcWire := DontCare + dcWire.ref + case _ => alt.ref + } + pushOp(DefPrim(sourceInfo, d, MultiplexOp, cond.ref, conRef, altRef)) + } +} diff --git a/core/src/main/scala/chisel3/Num.scala b/core/src/main/scala/chisel3/Num.scala new file mode 100644 index 00000000..7a6b0744 --- /dev/null +++ b/core/src/main/scala/chisel3/Num.scala @@ -0,0 +1,308 @@ +// See LICENSE for license details. + +package chisel3 + +import chisel3.internal.ChiselException +import chisel3.internal.firrtl.{BinaryPoint, KnownBinaryPoint} + +import scala.language.experimental.macros +import chisel3.internal.sourceinfo.{SourceInfo, SourceInfoTransform} + +// scalastyle:off method.name + +// REVIEW TODO: Further discussion needed on what Num actually is. + +/** Abstract trait defining operations available on numeric-like hardware data types. + * + * @tparam T the underlying type of the number + * @groupdesc Arithmetic Arithmetic hardware operators + * @groupdesc Comparison Comparison hardware operators + * @groupdesc Logical Logical hardware operators + * @define coll numeric-like type + * @define numType hardware type + * @define canHaveHighCost can result in significant cycle time and area costs + * @define canGenerateA This method generates a + * @define singleCycleMul @note $canGenerateA fully combinational multiplier which $canHaveHighCost. + * @define singleCycleDiv @note $canGenerateA fully combinational divider which $canHaveHighCost. + * @define maxWidth @note The width of the returned $numType is `max(width of this, width of that)`. + * @define maxWidthPlusOne @note The width of the returned $numType is `max(width of this, width of that) + 1`. + * @define sumWidth @note The width of the returned $numType is `width of this` + `width of that`. + * @define unchangedWidth @note The width of the returned $numType is unchanged, i.e., the `width of this`. + */ +trait Num[T <: Data] { + self: Num[T] => + // def << (b: T): T + // def >> (b: T): T + //def unary_-(): T + + // REVIEW TODO: double check ops conventions against FIRRTL + + /** Addition operator + * + * @param that a $numType + * @return the sum of this $coll and `that` + * $maxWidth + * @group Arithmetic + */ + final def + (that: T): T = macro SourceInfoTransform.thatArg + + /** @group SourceInfoTransformMacro */ + def do_+ (that: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T + + /** Multiplication operator + * + * @param that a $numType + * @return the product of this $coll and `that` + * $sumWidth + * $singleCycleMul + * @group Arithmetic + */ + final def * (that: T): T = macro SourceInfoTransform.thatArg + + /** @group SourceInfoTransformMacro */ + def do_* (that: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T + + /** Division operator + * + * @param that a $numType + * @return the quotient of this $coll divided by `that` + * $singleCycleDiv + * @todo full rules + * @group Arithmetic + */ + final def / (that: T): T = macro SourceInfoTransform.thatArg + + /** @group SourceInfoTransformMacro */ + def do_/ (that: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T + + /** Modulo operator + * + * @param that a $numType + * @return the remainder of this $coll divided by `that` + * $singleCycleDiv + * @group Arithmetic + */ + final def % (that: T): T = macro SourceInfoTransform.thatArg + + /** @group SourceInfoTransformMacro */ + def do_% (that: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T + + /** Subtraction operator + * + * @param that a $numType + * @return the difference of this $coll less `that` + * $maxWidthPlusOne + * @group Arithmetic + */ + final def - (that: T): T = macro SourceInfoTransform.thatArg + + /** @group SourceInfoTransformMacro */ + def do_- (that: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T + + /** Less than operator + * + * @param that a $numType + * @return a hardware [[Bool]] asserted if this $coll is less than `that` + * @group Comparison + */ + final def < (that: T): Bool = macro SourceInfoTransform.thatArg + + /** @group SourceInfoTransformMacro */ + def do_< (that: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool + + /** Less than or equal to operator + * + * @param that a $numType + * @return a hardware [[Bool]] asserted if this $coll is less than or equal to `that` + * @group Comparison + */ + final def <= (that: T): Bool = macro SourceInfoTransform.thatArg + + /** @group SourceInfoTransformMacro */ + def do_<= (that: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool + + /** Greater than operator + * + * @param that a hardware component + * @return a hardware [[Bool]] asserted if this $coll is greater than `that` + * @group Comparison + */ + final def > (that: T): Bool = macro SourceInfoTransform.thatArg + + /** @group SourceInfoTransformMacro */ + def do_> (that: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool + + /** Greater than or equal to operator + * + * @param that a hardware component + * @return a hardware [[Bool]] asserted if this $coll is greather than or equal to `that` + * @group Comparison + */ + final def >= (that: T): Bool = macro SourceInfoTransform.thatArg + + /** @group SourceInfoTransformMacro */ + def do_>= (that: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool + + /** Absolute value operator + * + * @return a $numType with a value equal to the absolute value of this $coll + * $unchangedWidth + * @group Arithmetic + */ + final def abs(): T = macro SourceInfoTransform.noArg + + /** @group SourceInfoTransformMacro */ + def do_abs(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T + + /** Minimum operator + * + * @param that a hardware $coll + * @return a $numType with a value equal to the mimimum value of this $coll and `that` + * $maxWidth + * @group Arithmetic + */ + final def min(that: T): T = macro SourceInfoTransform.thatArg + + /** @group SourceInfoTransformMacro */ + def do_min(that: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = + Mux(this < that, this.asInstanceOf[T], that) + + /** Maximum operator + * + * @param that a $numType + * @return a $numType with a value equal to the mimimum value of this $coll and `that` + * $maxWidth + * @group Arithmetic + */ + final def max(that: T): T = macro SourceInfoTransform.thatArg + + /** @group SourceInfoTransformMacro */ + def do_max(that: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = + Mux(this < that, that, this.asInstanceOf[T]) +} + +object Num extends NumObject + +/** NumbObject has a lot of convenience methods for converting between + * BigInts and Double and BigDecimal + * For backwards compatibility this is used with FixedPoint and Interval objects + * but is better used with the Num Object + * + */ +trait NumObject { + val MaxBitsBigIntToBigDecimal = 108 + val MaxBitsBigIntToDouble = 53 + + /** + * How to create a bigint from a double with a specific binaryPoint + * @param x a double value + * @param binaryPoint a binaryPoint that you would like to use + * @return + */ + def toBigInt(x: Double, binaryPoint: Int): BigInt = { + val multiplier = math.pow(2, binaryPoint) + val result = BigInt(math.round(x * multiplier)) + result + } + + /** + * How to create a bigint from a big decimal with a specific binaryPoint + * @param x a BigDecimal value + * @param binaryPoint a binaryPoint that you would like to use + * @return + */ + def toBigInt(x: Double, binaryPoint: BinaryPoint): BigInt = { + binaryPoint match { + case KnownBinaryPoint(n) => toBigInt(x, n) + case x => + throw new ChiselException(s"Error converting Double $x to BigInt, binary point must be known, not $x") + } + } + + /** + * How to create a bigint from a big decimal with a specific binaryPoint (int) + * @param x a BigDecimal value + * @param binaryPoint a binaryPoint that you would like to use + * @return + */ + def toBigInt(x: BigDecimal, binaryPoint: Int): BigInt = { + val multiplier = math.pow(2, binaryPoint) + val result = (x * multiplier).rounded.toBigInt() + result + } + + /** + * How to create a bigint from a big decimal with a specific binaryPoint + * @param value a BigDecimal value + * @param binaryPoint a binaryPoint that you would like to use + * @return + */ + def toBigInt(value: BigDecimal, binaryPoint: BinaryPoint): BigInt = { + binaryPoint match { + case KnownBinaryPoint(n) => toBigInt(value, n) + case x => + throw new ChiselException(s"Error converting BigDecimal $value to BigInt, binary point must be known, not $x") + } + } + + /** + * converts a bigInt with the given binaryPoint into the double representation + * @param i a bigint + * @param binaryPoint the implied binaryPoint of @i + * @return + */ + def toDouble(i: BigInt, binaryPoint: Int): Double = { + if(i.bitLength >= 54) { + throw new ChiselException( + s"BigInt $i with bitlength ${i.bitLength} is too big, precision lost with > $MaxBitsBigIntToDouble bits" + ) + } + val multiplier = math.pow(2, binaryPoint) + val result = i.toDouble / multiplier + result + } + + /** + * converts a bigInt with the given binaryPoint into the double representation + * @param value a bigint + * @param binaryPoint the implied binaryPoint of @i + * @return + */ + def toDouble(value: BigInt, binaryPoint: BinaryPoint): Double = { + binaryPoint match { + case KnownBinaryPoint(n) => toDouble(value, n) + case x => + throw new ChiselException(s"Error converting BigDecimal $value to BigInt, binary point must be known, not $x") + } } + + /** + * converts a bigInt with the given binaryPoint into the BigDecimal representation + * @param value a bigint + * @param binaryPoint the implied binaryPoint of @i + * @return + */ + def toBigDecimal(value: BigInt, binaryPoint: Int): BigDecimal = { + if(value.bitLength > MaxBitsBigIntToBigDecimal) { + throw new ChiselException( + s"BigInt $value with bitlength ${value.bitLength} is too big, precision lost with > $MaxBitsBigIntToBigDecimal bits" + ) + } + val multiplier = BigDecimal(1.0) / BigDecimal(math.pow(2, binaryPoint)) + val result = BigDecimal(value) * multiplier + result + } + + /** + * converts a bigInt with the given binaryPoint into the BigDecimal representation + * @param value a bigint + * @param binaryPoint the implied binaryPoint of @i + * @return + */ + def toBigDecimal(value: BigInt, binaryPoint: BinaryPoint): BigDecimal = { + binaryPoint match { + case KnownBinaryPoint(n) => toBigDecimal(value, n) + case x => + throw new ChiselException(s"Error converting BigDecimal $value to BigInt, binary point must be known, not $x") + } + } +} \ No newline at end of file diff --git a/core/src/main/scala/chisel3/Printable.scala b/core/src/main/scala/chisel3/Printable.scala new file mode 100644 index 00000000..7add9166 --- /dev/null +++ b/core/src/main/scala/chisel3/Printable.scala @@ -0,0 +1,178 @@ +// See LICENSE for license details. + +package chisel3 + +import chisel3.internal.firrtl.Component + +import scala.collection.mutable + +import java.util.{ + MissingFormatArgumentException, + UnknownFormatConversionException +} + +/** Superclass of things that can be printed in the resulting circuit + * + * Usually created using the custom string interpolator `p"..."`. Printable string interpolation is + * similar to [[https://docs.scala-lang.org/overviews/core/string-interpolation.html String + * interpolation in Scala]] For example: + * {{{ + * printf(p"The value of wire = \$wire\n") + * }}} + * This is equivalent to writing: + * {{{ + * printf(p"The value of wire = %d\n", wire) + * }}} + * All Chisel data types have a method `.toPrintable` that gives a default pretty print that can be + * accessed via `p"..."`. This works even for aggregate types, for example: + * {{{ + * val myVec = VecInit(5.U, 10.U, 13.U) + * printf(p"myVec = \$myVec\n") + * // myVec = Vec(5, 10, 13) + * + * val myBundle = Wire(new Bundle { + * val foo = UInt() + * val bar = UInt() + * }) + * myBundle.foo := 3.U + * myBundle.bar := 11.U + * printf(p"myBundle = \$myBundle\n") + * // myBundle = Bundle(a -> 3, b -> 11) + * }}} + * Users can override the default behavior of `.toPrintable` in custom [[Bundle]] and [[Record]] + * types. + */ +// TODO Add support for names of Modules +// Currently impossible because unpack is called before the name is selected +// Could be implemented by adding a new format specifier to Firrtl (eg. %m) +// TODO Should we provide more functions like map and mkPrintable? +sealed abstract class Printable { + /** Unpack into format String and a List of String arguments (identifiers) + * @note This must be called after elaboration when Chisel nodes actually + * have names + */ + def unpack(ctx: Component): (String, Iterable[String]) + /** Allow for appending Printables like Strings */ + final def +(that: Printable): Printables = Printables(List(this, that)) + /** Allow for appending Strings to Printables */ + final def +(that: String): Printables = Printables(List(this, PString(that))) +} +object Printable { + /** Pack standard printf fmt, args* style into Printable + */ + def pack(fmt: String, data: Data*): Printable = { // scalastyle:ignore method.length + val args = data.toIterator + + // Error handling + def carrotAt(index: Int) = (" " * index) + "^" + def errorMsg(index: Int) = + s"""| fmt = "$fmt" + | ${carrotAt(index)} + | data = ${data mkString ", "}""".stripMargin + def getArg(i: Int): Data = { + if (!args.hasNext) { + val msg = "has no matching argument!\n" + errorMsg(i) + // Exception wraps msg in s"Format Specifier '$msg'" + throw new MissingFormatArgumentException(msg) + } + args.next() + } + + val pables = mutable.ListBuffer.empty[Printable] + var str = "" + var percent = false + for ((c, i) <- fmt.zipWithIndex) { + if (percent) { + val arg = c match { + case FirrtlFormat(x) => FirrtlFormat(x.toString, getArg(i)) + case 'n' => Name(getArg(i)) + case 'N' => FullName(getArg(i)) + case '%' => Percent + case x => + val msg = s"Illegal format specifier '$x'!\n" + errorMsg(i) + throw new UnknownFormatConversionException(msg) + } + pables += PString(str dropRight 1) // remove format % + pables += arg + str = "" + percent = false + } else { + str += c + percent = c == '%' + } + } + if (percent) { + val msg = s"Trailing %\n" + errorMsg(fmt.size - 1) + throw new UnknownFormatConversionException(msg) + } + require(!args.hasNext, + s"Too many arguments! More format specifier(s) expected!\n" + + errorMsg(fmt.size)) + + pables += PString(str) + Printables(pables) + } +} + +case class Printables(pables: Iterable[Printable]) extends Printable { + require(pables.hasDefiniteSize, "Infinite-sized iterables are not supported!") + final def unpack(ctx: Component): (String, Iterable[String]) = { + val (fmts, args) = pables.map(_ unpack ctx).unzip + (fmts.mkString, args.flatten) + } +} +/** Wrapper for printing Scala Strings */ +case class PString(str: String) extends Printable { + final def unpack(ctx: Component): (String, Iterable[String]) = + (str replaceAll ("%", "%%"), List.empty) +} +/** Superclass for Firrtl format specifiers for Bits */ +sealed abstract class FirrtlFormat(private[chisel3] val specifier: Char) extends Printable { + def bits: Bits + def unpack(ctx: Component): (String, Iterable[String]) = { + (s"%$specifier", List(bits.ref.fullName(ctx))) + } +} +object FirrtlFormat { + final val legalSpecifiers = List('d', 'x', 'b', 'c') + + def unapply(x: Char): Option[Char] = + Option(x) filter (x => legalSpecifiers contains x) + + /** Helper for constructing Firrtl Formats + * Accepts data to simplify pack + */ + def apply(specifier: String, data: Data): FirrtlFormat = { + val bits = data match { + case b: Bits => b + case d => throw new Exception(s"Trying to construct FirrtlFormat with non-bits $d!") + } + specifier match { + case "d" => Decimal(bits) + case "x" => Hexadecimal(bits) + case "b" => Binary(bits) + case "c" => Character(bits) + case c => throw new Exception(s"Illegal format specifier '$c'!") + } + } +} +/** Format bits as Decimal */ +case class Decimal(bits: Bits) extends FirrtlFormat('d') +/** Format bits as Hexidecimal */ +case class Hexadecimal(bits: Bits) extends FirrtlFormat('x') +/** Format bits as Binary */ +case class Binary(bits: Bits) extends FirrtlFormat('b') +/** Format bits as Character */ +case class Character(bits: Bits) extends FirrtlFormat('c') +/** Put innermost name (eg. field of bundle) */ +case class Name(data: Data) extends Printable { + final def unpack(ctx: Component): (String, Iterable[String]) = (data.ref.name, List.empty) +} +/** Put full name within parent namespace (eg. bundleName.field) */ +case class FullName(data: Data) extends Printable { + final def unpack(ctx: Component): (String, Iterable[String]) = (data.ref.fullName(ctx), List.empty) +} +/** Represents escaped percents */ +case object Percent extends Printable { + final def unpack(ctx: Component): (String, Iterable[String]) = ("%%", List.empty) +} diff --git a/core/src/main/scala/chisel3/Printf.scala b/core/src/main/scala/chisel3/Printf.scala new file mode 100644 index 00000000..0478e889 --- /dev/null +++ b/core/src/main/scala/chisel3/Printf.scala @@ -0,0 +1,102 @@ +// See LICENSE for license details. + +package chisel3 + +import scala.language.experimental.macros + +import chisel3.internal._ +import chisel3.internal.Builder.pushCommand +import chisel3.internal.firrtl._ +import chisel3.internal.sourceinfo.SourceInfo + +/** Prints a message in simulation + * + * See apply methods for use + */ +object printf { // scalastyle:ignore object.name + /** Helper for packing escape characters */ + private[chisel3] def format(formatIn: String): String = { + require(formatIn forall (c => c.toInt > 0 && c.toInt < 128), + "format strings must comprise non-null ASCII values") + def escaped(x: Char) = { + require(x.toInt >= 0, s"char ${x} to Int ${x.toInt} must be >= 0") + if (x == '"' || x == '\\') { + s"\\${x}" + } else if (x == '\n') { + "\\n" + } else if (x == '\t') { + "\\t" + } else { + require(x.toInt >= 32, s"char ${x} to Int ${x.toInt} must be >= 32") // TODO \xNN once FIRRTL issue #59 is resolved + x + } + } + formatIn map escaped mkString "" + } + + /** Prints a message in simulation + * + * Prints a message every cycle. If defined within the scope of a [[when]] block, the message + * will only be printed on cycles that the when condition is true. + * + * Does not fire when in reset (defined as the encapsulating Module's reset). If your definition + * of reset is not the encapsulating Module's reset, you will need to gate this externally. + * + * May be called outside of a Module (like defined in a function), uses the current default clock + * and reset. These can be overriden with [[withClockAndReset]]. + * + * ==Format Strings== + * + * This method expects a ''format string'' and an ''argument list'' in a similar style to printf + * in C. The format string expects a [[scala.Predef.String String]] that may contain ''format + * specifiers'' For example: + * {{{ + * printf("myWire has the value %d\n", myWire) + * }}} + * This prints the string "myWire has the value " followed by the current value of `myWire` (in + * decimal, followed by a newline. + * + * There must be exactly as many arguments as there are format specifiers + * + * ===Format Specifiers=== + * + * Format specifiers are prefixed by `%`. If you wish to print a literal `%`, use `%%`. + * - `%d` - Decimal + * - `%x` - Hexadecimal + * - `%b` - Binary + * - `%c` - 8-bit Character + * - `%n` - Name of a signal + * - `%N` - Full name of a leaf signal (in an aggregate) + * + * @param fmt printf format string + * @param data format string varargs containing data to print + */ + def apply(fmt: String, data: Bits*)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Unit = + apply(Printable.pack(fmt, data:_*)) + /** Prints a message in simulation + * + * Prints a message every cycle. If defined within the scope of a [[when]] block, the message + * will only be printed on cycles that the when condition is true. + * + * Does not fire when in reset (defined as the encapsulating Module's reset). If your definition + * of reset is not the encapsulating Module's reset, you will need to gate this externally. + * + * May be called outside of a Module (like defined in a function), uses the current default clock + * and reset. These can be overriden with [[withClockAndReset]]. + * + * @see [[Printable]] documentation + * @param pable [[Printable]] to print + */ + def apply(pable: Printable)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Unit = { + when (!Module.reset.asBool) { + printfWithoutReset(pable) + } + } + + private[chisel3] def printfWithoutReset(pable: Printable)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Unit = { // scalastyle:ignore line.size.limit + val clock = Builder.forcedClock + pushCommand(Printf(sourceInfo, clock.ref, pable)) + } + private[chisel3] def printfWithoutReset(fmt: String, data: Bits*)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Unit = // scalastyle:ignore line.size.limit + printfWithoutReset(Printable.pack(fmt, data:_*)) +} diff --git a/core/src/main/scala/chisel3/RawModule.scala b/core/src/main/scala/chisel3/RawModule.scala new file mode 100644 index 00000000..218022cc --- /dev/null +++ b/core/src/main/scala/chisel3/RawModule.scala @@ -0,0 +1,233 @@ +// See LICENSE for license details. + +package chisel3 + +import scala.collection.mutable.{ArrayBuffer, HashMap} +import scala.collection.JavaConversions._ +import scala.language.experimental.macros + +import chisel3.experimental.BaseModule +import chisel3.internal._ +import chisel3.internal.Builder._ +import chisel3.internal.firrtl._ +import chisel3.internal.sourceinfo.UnlocatableSourceInfo + +/** Abstract base class for Modules that contain Chisel RTL. + * This abstract base class is a user-defined module which does not include implicit clock and reset and supports + * multiple IO() declarations. + */ +abstract class RawModule(implicit moduleCompileOptions: CompileOptions) + extends BaseModule { + // + // RTL construction internals + // + private val _commands = ArrayBuffer[Command]() + private[chisel3] def addCommand(c: Command) { + require(!_closed, "Can't write to module after module close") + _commands += c + } + protected def getCommands = { + require(_closed, "Can't get commands before module close") + _commands.toSeq + } + + // + // Other Internal Functions + // + // For debuggers/testers, TODO: refactor out into proper public API + private var _firrtlPorts: Option[Seq[firrtl.Port]] = None + lazy val getPorts = _firrtlPorts.get + + val compileOptions = moduleCompileOptions + + private[chisel3] def namePorts(names: HashMap[HasId, String]): Unit = { + for (port <- getModulePorts) { + port.suggestedName.orElse(names.get(port)) match { + case Some(name) => + if (_namespace.contains(name)) { + Builder.error(s"""Unable to name port $port to "$name" in $this,""" + + " name is already taken by another port!") + } + port.setRef(ModuleIO(this, _namespace.name(name))) + case None => + Builder.error(s"Unable to name port $port in $this, " + + "try making it a public field of the Module") + port.setRef(ModuleIO(this, "")) + } + } + } + + + private[chisel3] override def generateComponent(): Component = { // scalastyle:ignore cyclomatic.complexity + require(!_closed, "Can't generate module more than once") + _closed = true + + val names = nameIds(classOf[RawModule]) + + // Ports get first naming priority, since they are part of a Module's IO spec + namePorts(names) + + // Then everything else gets named + for ((node, name) <- names) { + node.suggestName(name) + } + + // All suggestions are in, force names to every node. + for (id <- getIds) { + id match { + case id: BaseModule => id.forceName(default=id.desiredName, _namespace) + case id: MemBase[_] => id.forceName(default="_T", _namespace) + case id: Data => + if (id.isSynthesizable) { + id.topBinding match { + case OpBinding(_) | MemoryPortBinding(_) | PortBinding(_) | RegBinding(_) | WireBinding(_) => + id.forceName(default="_T", _namespace) + case _ => // don't name literals + } + } // else, don't name unbound types + } + id._onModuleClose + } + + val firrtlPorts = getModulePorts map { port: Data => + // Special case Vec to make FIRRTL emit the direction of its + // element. + // Just taking the Vec's specifiedDirection is a bug in cases like + // Vec(Flipped()), since the Vec's specifiedDirection is + // Unspecified. + val direction = port match { + case v: Vec[_] => v.specifiedDirection match { + case SpecifiedDirection.Input => SpecifiedDirection.Input + case SpecifiedDirection.Output => SpecifiedDirection.Output + case SpecifiedDirection.Flip => SpecifiedDirection.flip(v.sample_element.specifiedDirection) + case SpecifiedDirection.Unspecified => v.sample_element.specifiedDirection + } + case _ => port.specifiedDirection + } + + Port(port, direction) + } + _firrtlPorts = Some(firrtlPorts) + + // Generate IO invalidation commands to initialize outputs as unused, + // unless the client wants explicit control over their generation. + val invalidateCommands = { + if (!compileOptions.explicitInvalidate) { + getModulePorts map { port => DefInvalid(UnlocatableSourceInfo, port.ref) } + } else { + Seq() + } + } + val component = DefModule(this, name, firrtlPorts, invalidateCommands ++ getCommands) + _component = Some(component) + component + } + + private[chisel3] def initializeInParent(parentCompileOptions: CompileOptions): Unit = { + implicit val sourceInfo = UnlocatableSourceInfo + + if (!parentCompileOptions.explicitInvalidate) { + for (port <- getModulePorts) { + pushCommand(DefInvalid(sourceInfo, port.ref)) + } + } + } +} + +trait RequireAsyncReset extends MultiIOModule { + override private[chisel3] def mkReset: AsyncReset = AsyncReset() +} + +trait RequireSyncReset extends MultiIOModule { + override private[chisel3] def mkReset: Bool = Bool() +} + +/** Abstract base class for Modules, which behave much like Verilog modules. + * These may contain both logic and state which are written in the Module + * body (constructor). + * This abstract base class includes an implicit clock and reset. + * + * @note Module instantiations must be wrapped in a Module() call. + */ +abstract class MultiIOModule(implicit moduleCompileOptions: CompileOptions) + extends RawModule { + // Implicit clock and reset pins + final val clock: Clock = IO(Input(Clock())) + final val reset: Reset = IO(Input(mkReset)) + + private[chisel3] def mkReset: Reset = { + // Top module and compatibility mode use Bool for reset + val inferReset = _parent.isDefined && moduleCompileOptions.inferModuleReset + if (inferReset) Reset() else Bool() + } + + // Setup ClockAndReset + Builder.currentClock = Some(clock) + Builder.currentReset = Some(reset) + + private[chisel3] override def initializeInParent(parentCompileOptions: CompileOptions): Unit = { + implicit val sourceInfo = UnlocatableSourceInfo + + super.initializeInParent(parentCompileOptions) + clock := Builder.forcedClock + reset := Builder.forcedReset + } +} + +package internal { + + /** Legacy Module class that restricts IOs to just io, clock, and reset, and provides a constructor + * for threading through explicit clock and reset. + * + * While this class isn't planned to be removed anytime soon (there are benefits to restricting + * IO), the clock and reset constructors will be phased out. Recommendation is to wrap the module + * in a withClock/withReset/withClockAndReset block, or directly hook up clock or reset IO pins. + */ + abstract class LegacyModule(implicit moduleCompileOptions: CompileOptions) + extends MultiIOModule { + // These are to be phased out + protected var override_clock: Option[Clock] = None + protected var override_reset: Option[Bool] = None + + // IO for this Module. At the Scala level (pre-FIRRTL transformations), + // connections in and out of a Module may only go through `io` elements. + def io: Record + + // Allow access to bindings from the compatibility package + protected def _compatIoPortBound() = portsContains(io)// scalastyle:ignore method.name + + private[chisel3] override def namePorts(names: HashMap[HasId, String]): Unit = { + for (port <- getModulePorts) { + // This should already have been caught + if (!names.contains(port)) throwException(s"Unable to name port $port in $this") + val name = names(port) + port.setRef(ModuleIO(this, _namespace.name(name))) + } + } + + private[chisel3] override def generateComponent(): Component = { + _compatAutoWrapPorts() // pre-IO(...) compatibility hack + + // Restrict IO to just io, clock, and reset + require(io != null, "Module must have io") + require(portsContains(io), "Module must have io wrapped in IO(...)") + require((portsContains(clock)) && (portsContains(reset)), "Internal error, module did not have clock or reset as IO") // scalastyle:ignore line.size.limit + require(portsSize == 3, "Module must only have io, clock, and reset as IO") + + super.generateComponent() + } + + private[chisel3] override def initializeInParent(parentCompileOptions: CompileOptions): Unit = { + // Don't generate source info referencing parents inside a module, since this interferes with + // module de-duplication in FIRRTL emission. + implicit val sourceInfo = UnlocatableSourceInfo + + if (!parentCompileOptions.explicitInvalidate) { + pushCommand(DefInvalid(sourceInfo, io.ref)) + } + + clock := override_clock.getOrElse(Builder.forcedClock) + reset := override_reset.getOrElse(Builder.forcedReset) + } + } +} diff --git a/core/src/main/scala/chisel3/Reg.scala b/core/src/main/scala/chisel3/Reg.scala new file mode 100644 index 00000000..7129c389 --- /dev/null +++ b/core/src/main/scala/chisel3/Reg.scala @@ -0,0 +1,195 @@ +// See LICENSE for license details. + +package chisel3 + +import scala.language.experimental.macros + +import chisel3.internal._ +import chisel3.internal.Builder.pushCommand +import chisel3.internal.firrtl._ +import chisel3.internal.sourceinfo.SourceInfo + +/** Utility for constructing hardware registers + * + * The width of a `Reg` (inferred or not) is copied from the type template + * {{{ + * val r0 = Reg(UInt()) // width is inferred + * val r1 = Reg(UInt(8.W)) // width is set to 8 + * + * val r2 = Reg(Vec(4, UInt())) // width is inferred + * val r3 = Reg(Vec(4, UInt(8.W))) // width of each element is set to 8 + * + * class MyBundle { + * val unknown = UInt() + * val known = UInt(8.W) + * } + * val r4 = Reg(new MyBundle) + * // Width of r4.unknown is inferred + * // Width of r4.known is set to 8 + * }}} + * + */ +object Reg { + /** Construct a [[Reg]] from a type template with no initialization value (reset is ignored). + * Value will not change unless the [[Reg]] is given a connection. + * @param t The template from which to construct this wire + */ + def apply[T <: Data](t: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = { + if (compileOptions.declaredTypeMustBeUnbound) { + requireIsChiselType(t, "reg type") + } + val reg = t.cloneTypeFull + val clock = Node(Builder.forcedClock) + + reg.bind(RegBinding(Builder.forcedUserModule)) + pushCommand(DefReg(sourceInfo, reg, clock)) + reg + } + +} + +/** Utility for constructing one-cycle delayed versions of signals + * + * ''The width of a `RegNext` is not set based on the `next` or `init` connections'' for [[Element]] types. In the + * following example, the width of `bar` will not be set and will be inferred by the FIRRTL compiler. + * {{{ + * val foo = Reg(UInt(4.W)) // width is 4 + * val bar = RegNext(foo) // width is unset + * }}} + * + * If you desire an explicit width, do not use `RegNext` and instead use a register with a specified width: + * {{{ + * val foo = Reg(UInt(4.W)) // width is 4 + * val bar = Reg(chiselTypeOf(foo)) // width is 4 + * bar := foo + * }}} + * + * Also note that a `RegNext` of a [[Bundle]] ''will have it's width set'' for [[Aggregate]] types. + * {{{ + * class MyBundle extends Bundle { + * val x = UInt(4.W) + * } + * val foo = Wire(new MyBundle) // the width of foo.x is 4 + * val bar = RegNext(foo) // the width of bar.x is 4 + * }}} + */ +object RegNext { + /** Returns a register ''with an unset width'' connected to the signal `next` and with no reset value. */ + def apply[T <: Data](next: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = { + val model = (next match { + case next: Bits => next.cloneTypeWidth(Width()) + case next => next.cloneTypeFull + }).asInstanceOf[T] + val reg = Reg(model) + + requireIsHardware(next, "reg next") + reg := next + + reg + } + + /** Returns a register ''with an unset width'' connected to the signal `next` and with the reset value `init`. */ + def apply[T <: Data](next: T, init: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = { + val model = (next match { + case next: Bits => next.cloneTypeWidth(Width()) + case next => next.cloneTypeFull + }).asInstanceOf[T] + val reg = RegInit(model, init) // TODO: this makes NO sense + + requireIsHardware(next, "reg next") + reg := next + + reg + } +} + +/** Utility for constructing hardware registers with an initialization value. + * + * The register is set to the initialization value when the current implicit `reset` is high + * + * The two forms of `RegInit` differ in how the type and width of the resulting [[Reg]] are + * specified. + * + * ==Single Argument== + * The single argument form uses the argument to specify both the type and reset value. For + * non-literal [[Bits]], the width of the [[Reg]] will be inferred. For literal [[Bits]] and all + * non-Bits arguments, the type will be copied from the argument. See the following examples for + * more details: + * + * 1. Literal [[Bits]] initializer: width will be set to match + * {{{ + * val r1 = RegInit(1.U) // width will be inferred to be 1 + * val r2 = RegInit(1.U(8.W)) // width is set to 8 + * }}} + * + * 2. Non-Literal [[Element]] initializer - width will be inferred + * {{{ + * val x = Wire(UInt()) + * val y = Wire(UInt(8.W)) + * val r1 = RegInit(x) // width will be inferred + * val r2 = RegInit(y) // width is set to 8 + * }}} + * + * 3. [[Aggregate]] initializer - width will be set to match the aggregate + * + * {{{ + * class MyBundle extends Bundle { + * val unknown = UInt() + * val known = UInt(8.W) + * } + * val w1 = Reg(new MyBundle) + * val w2 = RegInit(w1) + * // Width of w2.unknown is inferred + * // Width of w2.known is set to 8 + * }}} + * + * ==Double Argument== + * The double argument form allows the type of the [[Reg]] and the default connection to be + * specified independently. + * + * The width inference semantics for `RegInit` with two arguments match those of [[Reg]]. The + * first argument to `RegInit` is the type template which defines the width of the `Reg` in + * exactly the same way as the only argument to [[Wire]]. + * + * More explicitly, you can reason about `RegInit` with multiple arguments as if it were defined + * as: + * {{{ + * def RegInit[T <: Data](t: T, init: T): T = { + * val x = Reg(t) + * x := init + * x + * } + * }}} + */ +object RegInit { + /** Construct a [[Reg]] from a type template initialized to the specified value on reset + * @param t The type template used to construct this [[Reg]] + * @param init The value the [[Reg]] is initialized to on reset + */ + def apply[T <: Data](t: T, init: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = { + if (compileOptions.declaredTypeMustBeUnbound) { + requireIsChiselType(t, "reg type") + } + val reg = t.cloneTypeFull + val clock = Builder.forcedClock + val reset = Builder.forcedReset + + reg.bind(RegBinding(Builder.forcedUserModule)) + requireIsHardware(init, "reg initializer") + pushCommand(DefRegInit(sourceInfo, reg, clock.ref, reset.ref, init.ref)) + reg + } + + /** Construct a [[Reg]] initialized on reset to the specified value. + * @param init Initial value that serves as a type template and reset value + */ + def apply[T <: Data](init: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = { + val model = (init match { + // If init is a literal without forced width OR any non-literal, let width be inferred + case init: Bits if !init.litIsForcedWidth.getOrElse(false) => init.cloneTypeWidth(Width()) + case init => init.cloneTypeFull + }).asInstanceOf[T] + RegInit(model, init) + } + +} diff --git a/core/src/main/scala/chisel3/SIntFactory.scala b/core/src/main/scala/chisel3/SIntFactory.scala new file mode 100644 index 00000000..c1c6b1db --- /dev/null +++ b/core/src/main/scala/chisel3/SIntFactory.scala @@ -0,0 +1,25 @@ +// See LICENSE for license details. + +package chisel3 + +import chisel3.internal.firrtl.{IntervalRange, SLit, Width} + +trait SIntFactory { + /** Create an SInt type with inferred width. */ + def apply(): SInt = apply(Width()) + /** Create a SInt type or port with fixed width. */ + def apply(width: Width): SInt = new SInt(width) + + /** Create a SInt with the specified range */ + def apply(range: IntervalRange): SInt = { + apply(range.getWidth) + } + + /** Create an SInt literal with specified width. */ + // scalastyle:off method.name + protected[chisel3] def Lit(value: BigInt, width: Width): SInt = { + val lit = SLit(value, width) + val result = new SInt(lit.width) + lit.bindLitArg(result) + } +} diff --git a/core/src/main/scala/chisel3/SeqUtils.scala b/core/src/main/scala/chisel3/SeqUtils.scala new file mode 100644 index 00000000..28f753b1 --- /dev/null +++ b/core/src/main/scala/chisel3/SeqUtils.scala @@ -0,0 +1,128 @@ +// See LICENSE for license details. + +package chisel3 + +import chisel3.experimental.FixedPoint +import chisel3.internal.throwException + +import scala.language.experimental.macros +import chisel3.internal.sourceinfo._ + +//scalastyle:off method.name + +private[chisel3] object SeqUtils { + /** Concatenates the data elements of the input sequence, in sequence order, together. + * The first element of the sequence forms the least significant bits, while the last element + * in the sequence forms the most significant bits. + * + * Equivalent to r(n-1) ## ... ## r(1) ## r(0). + */ + def asUInt[T <: Bits](in: Seq[T]): UInt = macro SourceInfoTransform.inArg + + /** @group SourceInfoTransformMacros */ + def do_asUInt[T <: Bits](in: Seq[T])(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = { + if (in.tail.isEmpty) { + in.head.asUInt + } else { + val left = asUInt(in.slice(0, in.length/2)) + val right = asUInt(in.slice(in.length/2, in.length)) + right ## left + } + } + + /** Outputs the number of elements that === true.B. + */ + def count(in: Seq[Bool]): UInt = macro SourceInfoTransform.inArg + + /** @group SourceInfoTransformMacros */ + def do_count(in: Seq[Bool])(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = in.size match { + case 0 => 0.U + case 1 => in.head + case n => + val sum = count(in take n/2) +& count(in drop n/2) + sum(BigInt(n).bitLength - 1, 0) + } + + /** Returns the data value corresponding to the first true predicate. + */ + def priorityMux[T <: Data](in: Seq[(Bool, T)]): T = macro SourceInfoTransform.inArg + + /** @group SourceInfoTransformMacros */ + def do_priorityMux[T <: Data](in: Seq[(Bool, T)]) + (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = { + if (in.size == 1) { + in.head._2 + } else { + Mux(in.head._1, in.head._2, priorityMux(in.tail)) + } + } + + /** Returns the data value corresponding to the lone true predicate. + * This is elaborated to firrtl using a structure that should be optimized into and and/or tree. + * + * @note assumes exactly one true predicate, results undefined otherwise + * FixedPoint values or aggregates containing FixedPoint values cause this optimized structure to be lost + */ + def oneHotMux[T <: Data](in: Iterable[(Bool, T)]): T = macro SourceInfoTransform.inArg + + //scalastyle:off method.length cyclomatic.complexity + /** @group SourceInfoTransformMacros */ + def do_oneHotMux[T <: Data](in: Iterable[(Bool, T)]) + (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = { + if (in.tail.isEmpty) { + in.head._2 + } + else { + val output = cloneSupertype(in.toSeq map { _._2}, "oneHotMux") + + def buildAndOrMultiplexor[TT <: Data](inputs: Iterable[(Bool, TT)]): T = { + val masked = for ((s, i) <- inputs) yield Mux(s, i.asUInt(), 0.U) + masked.reduceLeft(_ | _).asTypeOf(output) + } + + output match { + case _: SInt => + // SInt's have to be managed carefully so sign extension works + + val sInts: Iterable[(Bool, SInt)] = in.collect { case (s: Bool, f: SInt) => + (s, f.asTypeOf(output).asInstanceOf[SInt]) + } + + val masked = for ((s, i) <- sInts) yield Mux(s, i, 0.S) + masked.reduceLeft(_ | _).asTypeOf(output) + + case _: FixedPoint => + val (sels, possibleOuts) = in.toSeq.unzip + + val (intWidths, binaryPoints) = in.toSeq.map { case (_, o) => + val fo = o.asInstanceOf[FixedPoint] + require(fo.binaryPoint.known, "Mux1H requires width/binary points to be defined") + (fo.getWidth - fo.binaryPoint.get, fo.binaryPoint.get) + }.unzip + + if (intWidths.distinct.length == 1 && binaryPoints.distinct.length == 1) { + buildAndOrMultiplexor(in) + } + else { + val maxIntWidth = intWidths.max + val maxBP = binaryPoints.max + val inWidthMatched = Seq.fill(intWidths.length)(Wire(FixedPoint((maxIntWidth + maxBP).W, maxBP.BP))) + inWidthMatched.zipWithIndex foreach { case (e, idx) => e := possibleOuts(idx).asInstanceOf[FixedPoint] } + buildAndOrMultiplexor(sels.zip(inWidthMatched)) + } + + case _: Aggregate => + val allDefineWidth = in.forall { case (_, element) => element.widthOption.isDefined } + if(allDefineWidth) { + buildAndOrMultiplexor(in) + } + else { + throwException(s"Cannot Mux1H with aggregates with inferred widths") + } + + case _ => + buildAndOrMultiplexor(in) + } + } + } +} diff --git a/core/src/main/scala/chisel3/StrongEnum.scala b/core/src/main/scala/chisel3/StrongEnum.scala new file mode 100644 index 00000000..8edce4d8 --- /dev/null +++ b/core/src/main/scala/chisel3/StrongEnum.scala @@ -0,0 +1,343 @@ +// See LICENSE for license details. + +package chisel3.experimental + +import scala.language.experimental.macros +import scala.reflect.macros.blackbox.Context +import scala.collection.mutable +import chisel3._ +import chisel3.internal.Builder.pushOp +import chisel3.internal.firrtl.PrimOp._ +import chisel3.internal.firrtl._ +import chisel3.internal.sourceinfo._ +import chisel3.internal.{Binding, Builder, ChildBinding, ConstrainedBinding, InstanceId, throwException} +import firrtl.annotations._ + + +object EnumAnnotations { + /** An annotation for strong enum instances that are ''not'' inside of Vecs + * + * @param target the enum instance being annotated + * @param typeName the name of the enum's type (e.g. ''"mypackage.MyEnum"'') + */ + case class EnumComponentAnnotation(target: Named, enumTypeName: String) extends SingleTargetAnnotation[Named] { + def duplicate(n: Named): EnumComponentAnnotation = this.copy(target = n) + } + + case class EnumComponentChiselAnnotation(target: InstanceId, enumTypeName: String) extends ChiselAnnotation { + def toFirrtl: EnumComponentAnnotation = EnumComponentAnnotation(target.toNamed, enumTypeName) + } + + /** An annotation for Vecs of strong enums. + * + * The ''fields'' parameter deserves special attention, since it may be difficult to understand. Suppose you create a the following Vec: + + * {{{ + * VecInit(new Bundle { + * val e = MyEnum() + * val b = new Bundle { + * val inner_e = MyEnum() + * } + * val v = Vec(3, MyEnum()) + * } + * }}} + * + * Then, the ''fields'' parameter will be: ''Seq(Seq("e"), Seq("b", "inner_e"), Seq("v"))''. Note that for any Vec that doesn't contain Bundles, this field will simply be an empty Seq. + * + * @param target the Vec being annotated + * @param typeName the name of the enum's type (e.g. ''"mypackage.MyEnum"'') + * @param fields a list of all chains of elements leading from the Vec instance to its inner enum fields. + * + */ + case class EnumVecAnnotation(target: Named, typeName: String, fields: Seq[Seq[String]]) extends SingleTargetAnnotation[Named] { + def duplicate(n: Named) = this.copy(target = n) + } + + case class EnumVecChiselAnnotation(target: InstanceId, typeName: String, fields: Seq[Seq[String]]) extends ChiselAnnotation { + override def toFirrtl = EnumVecAnnotation(target.toNamed, typeName, fields) + } + + /** An annotation for enum types (rather than enum ''instances''). + * + * @param typeName the name of the enum's type (e.g. ''"mypackage.MyEnum"'') + * @param definition a map describing which integer values correspond to which enum names + */ + case class EnumDefAnnotation(typeName: String, definition: Map[String, BigInt]) extends NoTargetAnnotation + + case class EnumDefChiselAnnotation(typeName: String, definition: Map[String, BigInt]) extends ChiselAnnotation { + override def toFirrtl: Annotation = EnumDefAnnotation(typeName, definition) + } +} +import EnumAnnotations._ + + +abstract class EnumType(private val factory: EnumFactory, selfAnnotating: Boolean = true) extends Element { + override def toString: String = { + val bindingString = litOption match { + case Some(value) => factory.nameOfValue(value) match { + case Some(name) => s"($value=$name)" + case None => s"($value=(invalid))" + } + case _ => bindingToString + } + // Use getSimpleName instead of enumTypeName because for debugging purposes the fully qualified name isn't + // necessary (compared to for the Enum annotation), and it's more consistent with Bundle printing. + s"${factory.getClass.getSimpleName.init}$bindingString" + } + + override def cloneType: this.type = factory().asInstanceOf[this.type] + + private[chisel3] def compop(sourceInfo: SourceInfo, op: PrimOp, other: EnumType): Bool = { + requireIsHardware(this, "bits operated on") + requireIsHardware(other, "bits operated on") + + if(!this.typeEquivalent(other)) { + throwException(s"Enum types are not equivalent: ${this.enumTypeName}, ${other.enumTypeName}") + } + + pushOp(DefPrim(sourceInfo, Bool(), op, this.ref, other.ref)) + } + + private[chisel3] override def typeEquivalent(that: Data): Boolean = { + this.getClass == that.getClass && + this.factory == that.asInstanceOf[EnumType].factory + } + + private[chisel3] override def connectFromBits(that: Bits)(implicit sourceInfo: SourceInfo, + compileOptions: CompileOptions): Unit = { + this := factory.apply(that.asUInt) + } + + final def === (that: EnumType): Bool = macro SourceInfoTransform.thatArg + final def =/= (that: EnumType): Bool = macro SourceInfoTransform.thatArg + final def < (that: EnumType): Bool = macro SourceInfoTransform.thatArg + final def <= (that: EnumType): Bool = macro SourceInfoTransform.thatArg + final def > (that: EnumType): Bool = macro SourceInfoTransform.thatArg + final def >= (that: EnumType): Bool = macro SourceInfoTransform.thatArg + + // scalastyle:off line.size.limit method.name + def do_=== (that: EnumType)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, EqualOp, that) + def do_=/= (that: EnumType)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, NotEqualOp, that) + def do_< (that: EnumType)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, LessOp, that) + def do_> (that: EnumType)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, GreaterOp, that) + def do_<= (that: EnumType)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, LessEqOp, that) + def do_>= (that: EnumType)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, GreaterEqOp, that) + // scalastyle:on line.size.limit method.name + + override def do_asUInt(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = + pushOp(DefPrim(sourceInfo, UInt(width), AsUIntOp, ref)) + + protected[chisel3] override def width: Width = factory.width + + def isValid(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = { + if (litOption.isDefined) { + true.B + } else { + factory.all.map(this === _).reduce(_ || _) + } + } + + def next(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): this.type = { + if (litOption.isDefined) { + val index = factory.all.indexOf(this) + + if (index < factory.all.length-1) { + factory.all(index + 1).asInstanceOf[this.type] + } else { + factory.all.head.asInstanceOf[this.type] + } + } else { + val enums_with_nexts = factory.all zip (factory.all.tail :+ factory.all.head) + val next_enum = SeqUtils.priorityMux(enums_with_nexts.map { case (e,n) => (this === e, n) } ) + next_enum.asInstanceOf[this.type] + } + } + + private[chisel3] def bindToLiteral(num: BigInt, w: Width): Unit = { + val lit = ULit(num, w) + lit.bindLitArg(this) + } + + override private[chisel3] def bind(target: Binding, parentDirection: SpecifiedDirection = SpecifiedDirection.Unspecified): Unit = { + super.bind(target, parentDirection) + + // Make sure we only annotate hardware and not literals + if (selfAnnotating && isSynthesizable && topBindingOpt.get.isInstanceOf[ConstrainedBinding]) { + annotateEnum() + } + } + + // This function conducts a depth-wise search to find all enum-type fields within a vector or bundle (or vector of bundles) + private def enumFields(d: Aggregate): Seq[Seq[String]] = d match { + case v: Vec[_] => v.sample_element match { + case b: Bundle => enumFields (b) + case _ => Seq () + } + case b: Bundle => + b.elements.collect { + case (name, e: EnumType) if this.typeEquivalent(e) => Seq(Seq(name)) + case (name, v: Vec[_]) if this.typeEquivalent(v.sample_element) => Seq(Seq(name)) + case (name, b2: Bundle) => enumFields(b2).map(name +: _) + }.flatten.toSeq + } + + private def outerMostVec(d: Data = this): Option[Vec[_]] = { + val currentVecOpt = d match { + case v: Vec[_] => Some(v) + case _ => None + } + + d.binding match { + case Some(ChildBinding(parent)) => outerMostVec(parent) match { + case outer @ Some(_) => outer + case None => currentVecOpt + } + case _ => currentVecOpt + } + } + + private def annotateEnum(): Unit = { + val anno = outerMostVec() match { + case Some(v) => EnumVecChiselAnnotation(v, enumTypeName, enumFields(v)) + case None => EnumComponentChiselAnnotation(this, enumTypeName) + } + + if (!Builder.annotations.contains(anno)) { + annotate(anno) + } + + if (!Builder.annotations.contains(factory.globalAnnotation)) { + annotate(factory.globalAnnotation) + } + } + + protected def enumTypeName: String = factory.enumTypeName + + def toPrintable: Printable = FullName(this) // TODO: Find a better pretty printer +} + + +abstract class EnumFactory { + class Type extends EnumType(this) + object Type { + def apply(): Type = EnumFactory.this.apply() + } + + private var id: BigInt = 0 + private[chisel3] var width: Width = 0.W + + private case class EnumRecord(inst: Type, name: String) + private val enum_records = mutable.ArrayBuffer.empty[EnumRecord] + + private def enumNames = enum_records.map(_.name).toSeq + private def enumValues = enum_records.map(_.inst.litValue()).toSeq + private def enumInstances = enum_records.map(_.inst).toSeq + + private[chisel3] val enumTypeName = getClass.getName.init + + private[chisel3] def globalAnnotation: EnumDefChiselAnnotation = + EnumDefChiselAnnotation(enumTypeName, (enumNames, enumValues).zipped.toMap) + + def getWidth: Int = width.get + + def all: Seq[Type] = enumInstances + + private[chisel3] def nameOfValue(id: BigInt): Option[String] = { + enum_records.find(_.inst.litValue() == id).map(_.name) + } + + protected def Value: Type = macro EnumMacros.ValImpl // scalastyle:off method.name + protected def Value(id: UInt): Type = macro EnumMacros.ValCustomImpl // scalastyle:off method.name + + protected def do_Value(name: String): Type = { + val result = new Type + + // We have to use UnknownWidth here, because we don't actually know what the final width will be + result.bindToLiteral(id, UnknownWidth()) + + enum_records.append(EnumRecord(result, name)) + + width = (1 max id.bitLength).W + id += 1 + + result + } + + protected def do_Value(name: String, id: UInt): Type = { + // TODO: These throw ExceptionInInitializerError which can be confusing to the user. Get rid of the error, and just + // throw an exception + if (id.litOption.isEmpty) { + throwException(s"$enumTypeName defined with a non-literal type") + } + if (id.litValue() < this.id) { + throwException(s"Enums must be strictly increasing: $enumTypeName") + } + + this.id = id.litValue() + do_Value(name) + } + + def apply(): Type = new Type + + def apply(n: UInt)(implicit sourceInfo: SourceInfo, connectionCompileOptions: CompileOptions): Type = { + // scalastyle:off line.size.limit + if (n.litOption.isDefined) { + enumInstances.find(_.litValue == n.litValue) match { + case Some(result) => result + case None => throwException(s"${n.litValue} is not a valid value for $enumTypeName") + } + } else if (!n.isWidthKnown) { + throwException(s"Non-literal UInts being cast to $enumTypeName must have a defined width") + } else if (n.getWidth > this.getWidth) { + throwException(s"The UInt being cast to $enumTypeName is wider than $enumTypeName's width ($getWidth)") + } else { + Builder.warning(s"Casting non-literal UInt to $enumTypeName. You can check that its value is legal by calling isValid") + + val glue = Wire(new UnsafeEnum(width)) + glue := n + val result = Wire(new Type) + result := glue + result + } + } + // scalastyle:on line.size.limit +} + + +private[chisel3] object EnumMacros { + def ValImpl(c: Context) : c.Tree = { // scalastyle:off method.name + import c.universe._ + + // Much thanks to michael_s for this solution: + // stackoverflow.com/questions/18450203/retrieve-the-name-of-the-value-a-scala-macro-invocation-will-be-assigned-to + val term = c.internal.enclosingOwner + val name = term.name.decodedName.toString.trim + + if (name.contains(" ")) { + c.abort(c.enclosingPosition, "Value cannot be called without assigning to an enum") + } + + q"""this.do_Value($name)""" + } + + def ValCustomImpl(c: Context)(id: c.Expr[UInt]): c.universe.Tree = { // scalastyle:off method.name + import c.universe._ + + val term = c.internal.enclosingOwner + val name = term.name.decodedName.toString.trim + + if (name.contains(" ")) { + c.abort(c.enclosingPosition, "Value cannot be called without assigning to an enum") + } + + q"""this.do_Value($name, $id)""" + } +} + + +// This is an enum type that can be connected directly to UInts. It is used as a "glue" to cast non-literal UInts +// to enums. +private[chisel3] class UnsafeEnum(override val width: Width) extends EnumType(UnsafeEnum, selfAnnotating = false) { + override def cloneType: this.type = new UnsafeEnum(width).asInstanceOf[this.type] +} +private object UnsafeEnum extends EnumFactory diff --git a/core/src/main/scala/chisel3/UIntFactory.scala b/core/src/main/scala/chisel3/UIntFactory.scala new file mode 100644 index 00000000..3868962b --- /dev/null +++ b/core/src/main/scala/chisel3/UIntFactory.scala @@ -0,0 +1,47 @@ +// See LICENSE for license details. + +package chisel3 + +import chisel3.internal.firrtl.{IntervalRange, KnownWidth, ULit, UnknownWidth, Width} +import firrtl.Utils +import firrtl.constraint.IsKnown +import firrtl.ir.{Closed, IntWidth, Open} + +// This is currently a factory because both Bits and UInt inherit it. +trait UIntFactory { + /** Create a UInt type with inferred width. */ + def apply(): UInt = apply(Width()) + /** Create a UInt port with specified width. */ + def apply(width: Width): UInt = new UInt(width) + + /** Create a UInt literal with specified width. */ + // scalastyle:off method.name + protected[chisel3] def Lit(value: BigInt, width: Width): UInt = { + val lit = ULit(value, width) + val result = new UInt(lit.width) + // Bind result to being an Literal + lit.bindLitArg(result) + } + /** Create a UInt with the specified range, validate that range is effectively > 0 + */ + //scalastyle:off cyclomatic.complexity + def apply(range: IntervalRange): UInt = { + // Check is only done against lower bound because range will already insist that range high >= low + range.lowerBound match { + case Closed(bound) if bound < 0 => + throw new ChiselException(s"Attempt to create UInt with closed lower bound of $bound, must be > 0") + case Open(bound) if bound < -1 => + throw new ChiselException(s"Attempt to create UInt with open lower bound of $bound, must be > -1") + case _ => + } + + // because this is a UInt we don't have to take into account the lower bound + val newWidth = if(range.upperBound.isInstanceOf[IsKnown]) { + KnownWidth(Utils.getUIntWidth(range.maxAdjusted.get).max(1)) // max(1) handles range"[0,0]" + } else { + UnknownWidth() + } + + apply(newWidth) + } +} diff --git a/core/src/main/scala/chisel3/When.scala b/core/src/main/scala/chisel3/When.scala new file mode 100644 index 00000000..ea243bbe --- /dev/null +++ b/core/src/main/scala/chisel3/When.scala @@ -0,0 +1,85 @@ +// See LICENSE for license details. + +package chisel3 + +import scala.language.experimental.macros + +import chisel3.internal._ +import chisel3.internal.Builder.pushCommand +import chisel3.internal.firrtl._ +import chisel3.internal.sourceinfo.{SourceInfo} + +object when { // scalastyle:ignore object.name + /** Create a `when` condition block, where whether a block of logic is + * executed or not depends on the conditional. + * + * @param cond condition to execute upon + * @param block logic that runs only if `cond` is true + * + * @example + * {{{ + * when ( myData === 3.U ) { + * // Some logic to run when myData equals 3. + * } .elsewhen ( myData === 1.U ) { + * // Some logic to run when myData equals 1. + * } .otherwise { + * // Some logic to run when myData is neither 3 nor 1. + * } + * }}} + */ + + def apply(cond: => Bool)(block: => Any)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): WhenContext = { // scalastyle:ignore line.size.limit + new WhenContext(sourceInfo, Some(() => cond), block) + } +} + +/** A WhenContext may represent a when, and elsewhen, or an + * otherwise. Since FIRRTL does not have an "elsif" statement, + * alternatives must be mapped to nested if-else statements inside + * the alternatives of the preceeding condition. In order to emit + * proper FIRRTL, it is necessary to keep track of the depth of + * nesting of the FIRRTL whens. Due to the "thin frontend" nature of + * Chisel3, it is not possible to know if a when or elsewhen has a + * succeeding elsewhen or otherwise; therefore, this information is + * added by preprocessing the command queue. + */ +final class WhenContext(sourceInfo: SourceInfo, cond: Option[() => Bool], block: => Any, firrtlDepth: Int = 0) { + + /** This block of logic gets executed if above conditions have been + * false and this condition is true. The lazy argument pattern + * makes it possible to delay evaluation of cond, emitting the + * declaration and assignment of the Bool node of the predicate in + * the correct place. + */ + def elsewhen (elseCond: => Bool)(block: => Any)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): WhenContext = { // scalastyle:ignore line.size.limit + new WhenContext(sourceInfo, Some(() => elseCond), block, firrtlDepth + 1) + } + + /** This block of logic gets executed only if the above conditions + * were all false. No additional logic blocks may be appended past + * the `otherwise`. The lazy argument pattern makes it possible to + * delay evaluation of cond, emitting the declaration and + * assignment of the Bool node of the predicate in the correct + * place. + */ + def otherwise(block: => Any)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Unit = + new WhenContext(sourceInfo, None, block, firrtlDepth + 1) + + /* + * + */ + if (firrtlDepth > 0) { pushCommand(AltBegin(sourceInfo)) } + cond.foreach( c => pushCommand(WhenBegin(sourceInfo, c().ref)) ) + Builder.whenDepth += 1 + try { + block + } catch { + case ret: scala.runtime.NonLocalReturnControl[_] => + throwException("Cannot exit from a when() block with a \"return\"!" + + " Perhaps you meant to use Mux or a Wire as a return value?" + ) + } + Builder.whenDepth -= 1 + cond.foreach( c => pushCommand(WhenEnd(sourceInfo,firrtlDepth)) ) + if (cond.isEmpty) { pushCommand(OtherwiseEnd(sourceInfo,firrtlDepth)) } +} diff --git a/core/src/main/scala/chisel3/aop/Aspect.scala b/core/src/main/scala/chisel3/aop/Aspect.scala new file mode 100644 index 00000000..9f10a0dd --- /dev/null +++ b/core/src/main/scala/chisel3/aop/Aspect.scala @@ -0,0 +1,40 @@ +// See LICENSE for license details. + +package chisel3.aop + +import chisel3.RawModule +import firrtl.annotations.{Annotation, NoTargetAnnotation} +import firrtl.options.Unserializable +import firrtl.AnnotationSeq + +/** Represents an aspect of a Chisel module, by specifying + * what behavior should be done to instance, via the FIRRTL Annotation Mechanism + * @tparam T Type of top-level module + */ +abstract class Aspect[T <: RawModule] extends Annotation with Unserializable with NoTargetAnnotation { + /** Convert this Aspect to a seq of FIRRTL annotation + * @param top + * @return + */ + def toAnnotation(top: T): AnnotationSeq + + /** Called by [[chisel3.stage.phases.AspectPhase]] to resolve this Aspect into annotations + * @param top + * @return + */ + private[chisel3] def resolveAspect(top: RawModule): AnnotationSeq = { + toAnnotation(top.asInstanceOf[T]) + } +} + +/** Holds utility functions for Aspect stuff */ +object Aspect { + + /** Converts elaborated Chisel components to FIRRTL modules + * @param chiselIR + * @return + */ + def getFirrtl(chiselIR: chisel3.internal.firrtl.Circuit): firrtl.ir.Circuit = { + chisel3.internal.firrtl.Converter.convert(chiselIR) + } +} diff --git a/core/src/main/scala/chisel3/core/package.scala b/core/src/main/scala/chisel3/core/package.scala new file mode 100644 index 00000000..92c4617b --- /dev/null +++ b/core/src/main/scala/chisel3/core/package.scala @@ -0,0 +1,288 @@ +// See LICENSE for license details. + +package chisel3 + +/** + * These definitions exist to deal with those clients that relied on chisel3.core. + * They are deprecated and will be removed in the future. + */ +package object core { + + @deprecated("Use the version in chisel3._", "3.2") + val CompileOptions = chisel3.CompileOptions + + @deprecated("Use the version in chisel3._", "3.2") + val Input = chisel3.Input + @deprecated("Use the version in chisel3._", "3.2") + val Output = chisel3.Output + @deprecated("Use the version in chisel3._", "3.2") + val Flipped = chisel3.Flipped + @deprecated("Use the version in chisel3._", "3.2") + val chiselTypeOf = chisel3.chiselTypeOf + + @deprecated("Use the version in chisel3._", "3.2") + type Data = chisel3.Data + + @deprecated("Use the version in chisel3._", "3.2") + val WireDefault = chisel3.WireDefault + + @deprecated("Use the version in chisel3._", "3.2") + val Clock = chisel3.Clock + @deprecated("Use the version in chisel3._", "3.2") + type Clock = chisel3.Clock + + @deprecated("Use the version in chisel3._", "3.2") + type Reset = chisel3.Reset + + @deprecated("Use the version in chisel3._", "3.2") + type Aggregate = chisel3.Aggregate + + @deprecated("Use the version in chisel3._", "3.2") + val Vec = chisel3.Vec + @deprecated("Use the version in chisel3._", "3.2") + val VecInit = chisel3.VecInit + @deprecated("Use the version in chisel3._", "3.2") + type Vec[T <: Data] = chisel3.Vec[T] + @deprecated("Use the version in chisel3._", "3.2") + type VecLike[T <: Data] = chisel3.VecLike[T] + @deprecated("Use the version in chisel3._", "3.2") + type Bundle = chisel3.Bundle + @deprecated("Use the version in chisel3._", "3.2") + type IgnoreSeqInBundle = chisel3.IgnoreSeqInBundle + @deprecated("Use the version in chisel3._", "3.2") + type Record = chisel3.Record + + @deprecated("Use the version in chisel3._", "3.2") + val assert = chisel3.assert + + @deprecated("Use the version in chisel3._", "3.2") + type Element = chisel3.Element + @deprecated("Use the version in chisel3._", "3.2") + type Bits = chisel3.Bits + + // These provide temporary compatibility for those who foolishly imported from chisel3.core + @deprecated("Avoid importing from chisel3.core, these are not public APIs and may change at any time. " + + " Use chisel3.RawModule instead. This alias will be removed in 3.3.", "since the beginning of time") + type RawModule = chisel3.RawModule + @deprecated("Avoid importing from chisel3.core, these are not public APIs and may change at any time. " + + "Use chisel3.MultiIOModule instead. This alias will be removed in 3.3.", "since the beginning of time") + type MultiIOModule = chisel3.MultiIOModule + @deprecated("Avoid importing from chisel3.core, these are not public APIs and may change at any time. " + + " Use chisel3.RawModule instead. This alias will be removed in 3.3.", "since the beginning of time") + type UserModule = chisel3.RawModule + @deprecated("Avoid importing from chisel3.core, these are not public APIs and may change at any time. " + + "Use chisel3.MultiIOModule instead. This alias will be removed in 3.3.", "since the beginning of time") + type ImplicitModule = chisel3.MultiIOModule + + @deprecated("Use the version in chisel3._", "3.2") + val Bits = chisel3.Bits + @deprecated("Use the version in chisel3._", "3.2") + type Num[T <: chisel3.Data] = chisel3.Num[T] + @deprecated("Use the version in chisel3._", "3.2") + type UInt = chisel3.UInt + @deprecated("Use the version in chisel3._", "3.2") + val UInt = chisel3.UInt + @deprecated("Use the version in chisel3._", "3.2") + type SInt = chisel3.SInt + @deprecated("Use the version in chisel3._", "3.2") + val SInt = chisel3.SInt + @deprecated("Use the version in chisel3._", "3.2") + type Bool = chisel3.Bool + @deprecated("Use the version in chisel3._", "3.2") + val Bool = chisel3.Bool + @deprecated("Use the version in chisel3._", "3.2") + val Mux = chisel3.Mux + + @deprecated("Use the version in chisel3._", "3.2") + type BlackBox = chisel3.BlackBox + + @deprecated("Use the version in chisel3._", "3.2") + val Mem = chisel3.Mem + @deprecated("Use the version in chisel3._", "3.2") + type MemBase[T <: chisel3.Data] = chisel3.MemBase[T] + @deprecated("Use the version in chisel3._", "3.2") + type Mem[T <: chisel3.Data] = chisel3.Mem[T] + @deprecated("Use the version in chisel3._", "3.2") + val SyncReadMem = chisel3.SyncReadMem + @deprecated("Use the version in chisel3._", "3.2") + type SyncReadMem[T <: chisel3.Data] = chisel3.SyncReadMem[T] + + @deprecated("Use the version in chisel3._", "3.2") + val Module = chisel3.Module + @deprecated("Use the version in chisel3._", "3.2") + type Module = chisel3.Module + + @deprecated("Use the version in chisel3._", "3.2") + val printf = chisel3.printf + + @deprecated("Use the version in chisel3._", "3.2") + val RegNext = chisel3.RegNext + @deprecated("Use the version in chisel3._", "3.2") + val RegInit = chisel3.RegInit + @deprecated("Use the version in chisel3._", "3.2") + val Reg = chisel3.Reg + + @deprecated("Use the version in chisel3._", "3.2") + val when = chisel3.when + @deprecated("Use the version in chisel3._", "3.2") + type WhenContext = chisel3.WhenContext + + @deprecated("Use the version in chisel3._", "3.2") + type Printable = chisel3.Printable + @deprecated("Use the version in chisel3._", "3.2") + val Printable = chisel3.Printable + @deprecated("Use the version in chisel3._", "3.2") + type Printables = chisel3.Printables + @deprecated("Use the version in chisel3._", "3.2") + val Printables = chisel3.Printables + @deprecated("Use the version in chisel3._", "3.2") + type PString = chisel3.PString + @deprecated("Use the version in chisel3._", "3.2") + val PString = chisel3.PString + @deprecated("Use the version in chisel3._", "3.2") + type FirrtlFormat = chisel3.FirrtlFormat + @deprecated("Use the version in chisel3._", "3.2") + val FirrtlFormat = chisel3.FirrtlFormat + @deprecated("Use the version in chisel3._", "3.2") + type Decimal = chisel3.Decimal + @deprecated("Use the version in chisel3._", "3.2") + val Decimal = chisel3.Decimal + @deprecated("Use the version in chisel3._", "3.2") + type Hexadecimal = chisel3.Hexadecimal + val Hexadecimal = chisel3.Hexadecimal + @deprecated("Use the version in chisel3._", "3.2") + type Binary = chisel3.Binary + @deprecated("Use the version in chisel3._", "3.2") + val Binary = chisel3.Binary + @deprecated("Use the version in chisel3._", "3.2") + type Character = chisel3.Character + @deprecated("Use the version in chisel3._", "3.2") + val Character = chisel3.Character + @deprecated("Use the version in chisel3._", "3.2") + type Name = chisel3.Name + @deprecated("Use the version in chisel3._", "3.2") + val Name = chisel3.Name + @deprecated("Use the version in chisel3._", "3.2") + type FullName = chisel3.FullName + @deprecated("Use the version in chisel3._", "3.2") + val FullName = chisel3.FullName + @deprecated("Use the version in chisel3._", "3.2") + val Percent = chisel3.Percent + + @deprecated("Use the version in chisel3.experimental._", "3.2") + type Param = chisel3.experimental.Param + @deprecated("Use the version in chisel3.experimental._", "3.2") + type IntParam = chisel3.experimental.IntParam + @deprecated("Use the version in chisel3.experimental._", "3.2") + val IntParam = chisel3.experimental.IntParam + @deprecated("Use the version in chisel3.experimental._", "3.2") + type DoubleParam = chisel3.experimental.DoubleParam + @deprecated("Use the version in chisel3.experimental._", "3.2") + val DoubleParam = chisel3.experimental.DoubleParam + @deprecated("Use the version in chisel3.experimental._", "3.2") + type StringParam = chisel3.experimental.StringParam + @deprecated("Use the version in chisel3.experimental._", "3.2") + val StringParam = chisel3.experimental.StringParam + @deprecated("Use the version in chisel3.experimental._", "3.2") + type RawParam = chisel3.experimental.RawParam + @deprecated("Use the version in chisel3.experimental._", "3.2") + val RawParam = chisel3.experimental.RawParam + + @deprecated("Use the version in chisel3.experimental._", "3.2") + type Analog = chisel3.experimental.Analog + @deprecated("Use the version in chisel3.experimental._", "3.2") + val Analog = chisel3.experimental.Analog + + @deprecated("Use the version in chisel3._", "3.2") + implicit class fromIntToWidth(int: Int) extends chisel3.fromIntToWidth(int) + + @deprecated("Use the version in chisel3.experimental._", "3.2") + val attach = chisel3.experimental.attach + + @deprecated("Use the version in chisel3.experimental._", "3.2") + type EnumType = chisel3.experimental.EnumType + @deprecated("Use the version in chisel3.experimental._", "3.2") + type EnumFactory = chisel3.experimental.EnumFactory + @deprecated("Use the version in chisel3.experimental._", "3.2") + val EnumAnnotations = chisel3.experimental.EnumAnnotations + + @deprecated("Use the version in chisel3._", "3.2") + val withClockAndReset = chisel3.withClockAndReset + @deprecated("Use the version in chisel3._", "3.2") + val withClock = chisel3.withClock + @deprecated("Use the version in chisel3._", "3.2") + val withReset = chisel3.withReset + + @deprecated("Use the version in chisel3._", "3.2") + val dontTouch = chisel3.dontTouch + + @deprecated("Use the version in chisel3.experimental._", "3.2") + type BaseModule = chisel3.experimental.BaseModule + @deprecated("Use the version in chisel3.experimental._", "3.2") + type ExtModule = chisel3.experimental.ExtModule + + @deprecated("Use the version in chisel3.experimental._", "3.2") + val IO = chisel3.experimental.IO + + @deprecated("Use the version in chisel3.experimental._", "3.2") + type FixedPoint = chisel3.experimental.FixedPoint + @deprecated("Use the version in chisel3.experimental._", "3.2") + val FixedPoint = chisel3.experimental.FixedPoint + @deprecated("Use the version in chisel3.experimental._", "3.2") + implicit class fromDoubleToLiteral(double: Double) extends experimental.FixedPoint.Implicits.fromDoubleToLiteral(double) + @deprecated("Use the version in chisel3.experimental._", "3.2") + implicit class fromIntToBinaryPoint(int: Int) extends chisel3.fromIntToBinaryPoint(int) + @deprecated("Use the version in chisel3.experimental._", "3.2") + type RunFirrtlTransform = chisel3.experimental.RunFirrtlTransform + + @deprecated("Use the version in chisel3.experimental._", "3.2") + val annotate = chisel3.experimental.annotate + + @deprecated("Use the version in chisel3.experimental._", "3.2") + val DataMirror = chisel3.experimental.DataMirror + @deprecated("Use the version in chisel3._", "3.2") + type ActualDirection = chisel3.ActualDirection + @deprecated("Use the version in chisel3._", "3.2") + val ActualDirection = chisel3.ActualDirection + + @deprecated("Use the version in chisel3.internal._", "3.2") + val requireIsHardware = chisel3.internal.requireIsHardware + @deprecated("Use the version in chisel3.internal._", "3.2") + val requireIsChiselType = chisel3.internal.requireIsChiselType + @deprecated("Use the version in chisel3.internal._", "3.2") + val BiConnect = chisel3.internal.BiConnect + @deprecated("Use the version in chisel3.internal._", "3.2") + val MonoConnect = chisel3.internal.MonoConnect + @deprecated("Use the version in chisel3.internal._", "3.2") + val BindingDirection = chisel3.internal.BindingDirection + @deprecated("Use the version in chisel3.internal._", "3.2") + type Binding = chisel3.internal.Binding + @deprecated("Use the version in chisel3.internal._", "3.2") + type TopBinding = chisel3.internal.TopBinding + @deprecated("Use the version in chisel3.internal._", "3.2") + type UnconstrainedBinding = chisel3.internal.UnconstrainedBinding + @deprecated("Use the version in chisel3.internal._", "3.2") + type ConstrainedBinding = chisel3.internal.ConstrainedBinding + @deprecated("Use the version in chisel3.internal._", "3.2") + type ReadOnlyBinding = chisel3.internal.ReadOnlyBinding + @deprecated("Use the version in chisel3.internal._", "3.2") + type OpBinding = chisel3.internal.OpBinding + @deprecated("Use the version in chisel3.internal._", "3.2") + type MemoryPortBinding = chisel3.internal.MemoryPortBinding + @deprecated("Use the version in chisel3.internal._", "3.2") + type PortBinding = chisel3.internal.PortBinding + @deprecated("Use the version in chisel3.internal._", "3.2") + type RegBinding = chisel3.internal.RegBinding + @deprecated("Use the version in chisel3.internal._", "3.2") + type WireBinding = chisel3.internal.WireBinding + @deprecated("Use the version in chisel3.internal._", "3.2") + type ChildBinding = chisel3.internal.ChildBinding + @deprecated("Use the version in chisel3.internal._", "3.2") + type DontCareBinding = chisel3.internal.DontCareBinding + @deprecated("Use the version in chisel3.internal._", "3.2") + type LitBinding = chisel3.internal.LitBinding + @deprecated("Use the version in chisel3.internal._", "3.2") + type ElementLitBinding = chisel3.internal.ElementLitBinding + @deprecated("Use the version in chisel3.internal._", "3.2") + type BundleLitBinding = chisel3.internal.BundleLitBinding +} diff --git a/core/src/main/scala/chisel3/dontTouch.scala b/core/src/main/scala/chisel3/dontTouch.scala new file mode 100644 index 00000000..5dfd9f19 --- /dev/null +++ b/core/src/main/scala/chisel3/dontTouch.scala @@ -0,0 +1,37 @@ +package chisel3 + +import chisel3.experimental.{ChiselAnnotation, annotate, requireIsHardware} +import firrtl.transforms.DontTouchAnnotation + +/** Marks that a signal should not be removed by Chisel and Firrtl optimization passes + * + * @example {{{ + * class MyModule extends Module { + * val io = IO(new Bundle { + * val a = Input(UInt(32.W)) + * val b = Output(UInt(32.W)) + * }) + * io.b := io.a + * val dead = io.a +% 1.U // normally dead would be pruned by DCE + * dontTouch(dead) // Marking it as such will preserve it + * } + * }}} + * @note Calling this on [[Data]] creates an annotation that Chisel emits to a separate annotations + * file. This file must be passed to FIRRTL independently of the `.fir` file. The execute methods + * in [[chisel3.Driver]] will pass the annotations to FIRRTL automatically. + */ +object dontTouch { // scalastyle:ignore object.name + /** Marks a signal to be preserved in Chisel and Firrtl + * + * @note Requires the argument to be bound to hardware + * @param data The signal to be marked + * @return Unmodified signal `data` + */ + def apply[T <: Data](data: T)(implicit compileOptions: CompileOptions): T = { + if (compileOptions.checkSynthesizable) { + requireIsHardware(data, "Data marked dontTouch") + } + annotate(new ChiselAnnotation { def toFirrtl = DontTouchAnnotation(data.toNamed) }) + data + } +} diff --git a/core/src/main/scala/chisel3/experimental/Analog.scala b/core/src/main/scala/chisel3/experimental/Analog.scala new file mode 100644 index 00000000..37eb578d --- /dev/null +++ b/core/src/main/scala/chisel3/experimental/Analog.scala @@ -0,0 +1,85 @@ +// See LICENSE for license details. + +package chisel3.experimental + +import chisel3.internal.firrtl.Width +import chisel3.internal.sourceinfo.SourceInfo +import chisel3.internal._ +import chisel3.{ActualDirection, Bits, CompileOptions, Data, Element, PString, Printable, RawModule, SpecifiedDirection, UInt} + +import scala.collection.mutable + +/** Data type for representing bidirectional bitvectors of a given width + * + * Analog support is limited to allowing wiring up of Verilog BlackBoxes with bidirectional (inout) + * pins. There is currently no support for reading or writing of Analog types within Chisel code. + * + * Given that Analog is bidirectional, it is illegal to assign a direction to any Analog type. It + * is legal to "flip" the direction (since Analog can be a member of aggregate types) which has no + * effect. + * + * Analog types are generally connected using the bidirectional [[attach]] mechanism, but also + * support limited bulkconnect `<>`. Analog types are only allowed to be bulk connected *once* in a + * given module. This is to prevent any surprising consequences of last connect semantics. + * + * @note This API is experimental and subject to change + */ +final class Analog private (private[chisel3] val width: Width) extends Element { + require(width.known, "Since Analog is only for use in BlackBoxes, width must be known") + + override def toString: String = { + s"Analog$width$bindingToString" + } + + private[chisel3] override def typeEquivalent(that: Data): Boolean = + that.isInstanceOf[Analog] && this.width == that.width + + override def litOption: Option[BigInt] = None + + def cloneType: this.type = new Analog(width).asInstanceOf[this.type] + + // Used to enforce single bulk connect of Analog types, multi-attach is still okay + // Note that this really means 1 bulk connect per Module because a port can + // be connected in the parent module as well + private[chisel3] val biConnectLocs = mutable.Map.empty[RawModule, SourceInfo] + + // Define setter/getter pairing + // Analog can only be bound to Ports and Wires (and Unbound) + private[chisel3] override def bind(target: Binding, parentDirection: SpecifiedDirection) { + SpecifiedDirection.fromParent(parentDirection, specifiedDirection) match { + case SpecifiedDirection.Unspecified | SpecifiedDirection.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 + // See https://github.com/freechipsproject/chisel3/pull/946 + case SampleElementBinding(parent) => parent.topBinding + } + + targetTopBinding match { + case _: WireBinding | _: 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 = + throwException("Analog does not support asUInt") + + private[chisel3] override def connectFromBits(that: Bits)(implicit sourceInfo: SourceInfo, + compileOptions: CompileOptions): Unit = { + throwException("Analog does not support connectFromBits") + } + + def toPrintable: Printable = PString("Analog") +} + +/** Object that provides factory methods for [[Analog]] objects + * + * @note This API is experimental and subject to change + */ +object Analog { + def apply(width: Width): Analog = new Analog(width) +} + diff --git a/core/src/main/scala/chisel3/experimental/package.scala b/core/src/main/scala/chisel3/experimental/package.scala new file mode 100644 index 00000000..985f7715 --- /dev/null +++ b/core/src/main/scala/chisel3/experimental/package.scala @@ -0,0 +1,140 @@ +// See LICENSE for license details. + +package chisel3 + +/** Package for experimental features, which may have their API changed, be removed, etc. + * + * Because its contents won't necessarily have the same level of stability and support as + * non-experimental, you must explicitly import this package to use its contents. + */ +package object experimental { // scalastyle:ignore object.name + import scala.language.implicitConversions + import chisel3.internal.BaseModule + + // Implicit conversions for BlackBox Parameters + implicit def fromIntToIntParam(x: Int): IntParam = IntParam(BigInt(x)) + implicit def fromLongToIntParam(x: Long): IntParam = IntParam(BigInt(x)) + implicit def fromBigIntToIntParam(x: BigInt): IntParam = IntParam(x) + implicit def fromDoubleToDoubleParam(x: Double): DoubleParam = DoubleParam(x) + implicit def fromStringToStringParam(x: String): StringParam = StringParam(x) + + type ChiselEnum = EnumFactory + + @deprecated("Use the version in chisel3._", "3.2") + val withClockAndReset = chisel3.withClockAndReset + @deprecated("Use the version in chisel3._", "3.2") + val withClock = chisel3.withClock + @deprecated("Use the version in chisel3._", "3.2") + val withReset = chisel3.withReset + + // Rocket Chip-style clonemodule + + /** A record containing the results of CloneModuleAsRecord + * The apply method is retrieves the element with the supplied name. + */ + type ClonePorts = BaseModule.ClonePorts + + object CloneModuleAsRecord { + /** Clones an existing module and returns a record of all its top-level ports. + * Each element of the record is named with a string matching the + * corresponding port's name and shares the port's type. + * @example {{{ + * val q1 = Module(new Queue(UInt(32.W), 2)) + * val q2_io = CloneModuleAsRecord(q1)("io").asInstanceOf[q1.io.type] + * q2_io.enq <> q1.io.deq + * }}} + */ + def apply(proto: BaseModule)(implicit sourceInfo: chisel3.internal.sourceinfo.SourceInfo, compileOptions: CompileOptions): ClonePorts = { // scalastyle:ignore line.size.limit + BaseModule.cloneIORecord(proto) + } + } + + val requireIsHardware = chisel3.internal.requireIsHardware + val requireIsChiselType = chisel3.internal.requireIsChiselType + type Direction = ActualDirection + val Direction = ActualDirection + + implicit class ChiselRange(val sc: StringContext) extends AnyVal { + + import scala.language.experimental.macros + + /** Specifies a range using mathematical range notation. Variables can be interpolated using + * standard string interpolation syntax. + * @example {{{ + * UInt(range"[0, 2)") + * UInt(range"[0, \$myInt)") + * UInt(range"[0, \${myInt + 2})") + * }}} + */ + def range(args: Any*): chisel3.internal.firrtl.IntervalRange = macro chisel3.internal.RangeTransform.apply + } + + class dump extends chisel3.internal.naming.dump // scalastyle:ignore class.name + class treedump extends chisel3.internal.naming.treedump // scalastyle:ignore class.name + /** Experimental macro for naming Chisel hardware values + * + * By default, Chisel uses reflection for naming which only works for public fields of `Bundle` + * and `Module` classes. Applying this macro annotation to a `class` or `object` enables Chisel + * to name any hardware values within the annotated `class` or `object. + * + * @example {{{ + * import chisel3._ + * import chisel3.experimental.chiselName + * + * @chiselName + * class MyModule extends Module { + * val io = IO(new Bundle { + * val in = Input(UInt(8.W)) + * val out = Output(UInt(8.W)) + * }) + * def createReg(): Unit = { + * // @chiselName allows Chisel to name this Reg + * val myReg = RegInit(io.in) + * io.out := myReg + * } + * createReg() + * } + * }}} + */ + class chiselName extends chisel3.internal.naming.chiselName // scalastyle:ignore class.name + /** Do not name instances of this type in [[chiselName]] + * + * By default, `chiselName` will include `val` names of instances of annotated classes as a + * prefix in final naming. Mixing in this trait to a `class`, `object`, or anonymous `class` + * instances will exclude the `val` name from `chiselName` naming. + * + * @example {{{ + * import chisel3._ + * import chisel3.experimental.{chiselName, NoChiselNamePrefix} + * + * // Note that this is not a Module + * @chiselName + * class Counter(w: Int) { + * val myReg = RegInit(0.U(w.W)) + * myReg := myReg + 1.U + * } + * + * @chiselName + * class MyModule extends Module { + * val io = IO(new Bundle { + * val out = Output(UInt(8.W)) + * }) + * // Name of myReg will be "counter0_myReg" + * val counter0 = new Counter(8) + * // Name of myReg will be "myReg" + * val counter1 = new Counter(8) with NoChiselNamePrefix + * io.out := counter0.myReg + counter1.myReg + * } + * }}} + */ + trait NoChiselNamePrefix + + object BundleLiterals { + implicit class AddBundleLiteralConstructor[T <: Bundle](x: T) { + //scalastyle:off method.name + def Lit(elems: (T => (Data, Data))*): T = { + x._makeLit(elems: _*) + } + } + } +} diff --git a/core/src/main/scala/chisel3/internal/BiConnect.scala b/core/src/main/scala/chisel3/internal/BiConnect.scala new file mode 100644 index 00000000..6b4c1070 --- /dev/null +++ b/core/src/main/scala/chisel3/internal/BiConnect.scala @@ -0,0 +1,333 @@ +// See LICENSE for license details. + +package chisel3.internal + +import chisel3._ +import chisel3.experimental.{Analog, BaseModule, attach} +import chisel3.internal.Builder.pushCommand +import chisel3.internal.firrtl.{Connect, DefInvalid} +import scala.language.experimental.macros +import chisel3.internal.sourceinfo._ + +/** +* BiConnect.connect executes a bidirectional connection element-wise. +* +* Note that the arguments are left and right (not source and sink) so the +* intent is for the operation to be commutative. +* +* The connect operation will recurse down the left Data (with the right Data). +* An exception will be thrown if a movement through the left cannot be matched +* in the right (or if the right side has extra fields). +* +* See elemConnect for details on how the root connections are issued. +* +*/ + +private[chisel3] object BiConnect { + // scalastyle:off method.name public.methods.have.type + // These are all the possible exceptions that can be thrown. + // These are from element-level connection + def BothDriversException = + BiConnectException(": Both Left and Right are drivers") + def NeitherDriverException = + BiConnectException(": Neither Left nor Right is a driver") + def UnknownDriverException = + BiConnectException(": Locally unclear whether Left or Right (both internal)") + def UnknownRelationException = + BiConnectException(": Left or Right unavailable to current module.") + // These are when recursing down aggregate types + def MismatchedVecException = + BiConnectException(": Left and Right are different length Vecs.") + def MissingLeftFieldException(field: String) = + BiConnectException(s".$field: Left Record missing field ($field).") + def MissingRightFieldException(field: String) = + BiConnectException(s": Right Record missing field ($field).") + def MismatchedException(left: String, right: String) = + BiConnectException(s": Left ($left) and Right ($right) have different types.") + def AttachAlreadyBulkConnectedException(sourceInfo: SourceInfo) = + BiConnectException(sourceInfo.makeMessage(": Analog previously bulk connected at " + _)) + def DontCareCantBeSink = + BiConnectException(": DontCare cannot be a connection sink (LHS)") + // scalastyle:on method.name public.methods.have.type + + /** This function is what recursively tries to connect a left and right together + * + * There is some cleverness in the use of internal try-catch to catch exceptions + * during the recursive decent and then rethrow them with extra information added. + * This gives the user a 'path' to where in the connections things went wrong. + */ + def connect(sourceInfo: SourceInfo, connectCompileOptions: CompileOptions, left: Data, right: Data, context_mod: RawModule): Unit = { // scalastyle:ignore line.size.limit cyclomatic.complexity method.length + (left, right) match { + // Handle element case (root case) + case (left_a: Analog, right_a: Analog) => + try { + markAnalogConnected(sourceInfo, left_a, context_mod) + markAnalogConnected(sourceInfo, right_a, context_mod) + } catch { // convert attach exceptions to BiConnectExceptions + case attach.AttachException(message) => throw BiConnectException(message) + } + attach.impl(Seq(left_a, right_a), context_mod)(sourceInfo) + case (left_a: Analog, DontCare) => + try { + markAnalogConnected(sourceInfo, left_a, context_mod) + } catch { // convert attach exceptions to BiConnectExceptions + case attach.AttachException(message) => throw BiConnectException(message) + } + pushCommand(DefInvalid(sourceInfo, left_a.lref)) + case (DontCare, right_a: Analog) => connect(sourceInfo, connectCompileOptions, right, left, context_mod) + case (left_e: Element, right_e: Element) => { + elemConnect(sourceInfo, connectCompileOptions, left_e, right_e, context_mod) + // TODO(twigg): Verify the element-level classes are connectable + } + // Handle Vec case + case (left_v: Vec[Data@unchecked], right_v: Vec[Data@unchecked]) => { + if (left_v.length != right_v.length) { + throw MismatchedVecException + } + for (idx <- 0 until left_v.length) { + try { + implicit val compileOptions = connectCompileOptions + connect(sourceInfo, connectCompileOptions, left_v(idx), right_v(idx), context_mod) + } catch { + case BiConnectException(message) => throw BiConnectException(s"($idx)$message") + } + } + } + // Handle Vec connected to DontCare + case (left_v: Vec[Data@unchecked], DontCare) => { + for (idx <- 0 until left_v.length) { + try { + implicit val compileOptions = connectCompileOptions + connect(sourceInfo, connectCompileOptions, left_v(idx), right, context_mod) + } catch { + case BiConnectException(message) => throw BiConnectException(s"($idx)$message") + } + } + } + // Handle DontCare connected to Vec + case (DontCare, right_v: Vec[Data@unchecked]) => { + for (idx <- 0 until right_v.length) { + try { + implicit val compileOptions = connectCompileOptions + connect(sourceInfo, connectCompileOptions, left, right_v(idx), context_mod) + } catch { + case BiConnectException(message) => throw BiConnectException(s"($idx)$message") + } + } + } + // Handle Records defined in Chisel._ code (change to NotStrict) + case (left_r: Record, right_r: Record) => (left_r.compileOptions, right_r.compileOptions) match { + case (ExplicitCompileOptions.NotStrict, _) => + left_r.bulkConnect(right_r)(sourceInfo, ExplicitCompileOptions.NotStrict) + case (_, ExplicitCompileOptions.NotStrict) => + left_r.bulkConnect(right_r)(sourceInfo, ExplicitCompileOptions.NotStrict) + case _ => recordConnect(sourceInfo, connectCompileOptions, left_r, right_r, context_mod) + } + + // Handle Records connected to DontCare (change to NotStrict) + case (left_r: Record, DontCare) => + left_r.compileOptions match { + case ExplicitCompileOptions.NotStrict => + left.bulkConnect(right)(sourceInfo, ExplicitCompileOptions.NotStrict) + case _ => + // For each field in left, descend with right + for ((field, left_sub) <- left_r.elements) { + try { + connect(sourceInfo, connectCompileOptions, left_sub, right, context_mod) + } catch { + case BiConnectException(message) => throw BiConnectException(s".$field$message") + } + } + } + case (DontCare, right_r: Record) => + right_r.compileOptions match { + case ExplicitCompileOptions.NotStrict => + left.bulkConnect(right)(sourceInfo, ExplicitCompileOptions.NotStrict) + case _ => + // For each field in left, descend with right + for ((field, right_sub) <- right_r.elements) { + try { + connect(sourceInfo, connectCompileOptions, left, right_sub, context_mod) + } catch { + case BiConnectException(message) => throw BiConnectException(s".$field$message") + } + } + } + + // Left and right are different subtypes of Data so fail + case (left, right) => throw MismatchedException(left.toString, right.toString) + } + } + + // Do connection of two Records + def recordConnect(sourceInfo: SourceInfo, + connectCompileOptions: CompileOptions, + left_r: Record, + right_r: Record, + context_mod: RawModule): Unit = { + // Verify right has no extra fields that left doesn't have + for((field, right_sub) <- right_r.elements) { + if(!left_r.elements.isDefinedAt(field)) { + if (connectCompileOptions.connectFieldsMustMatch) { + throw MissingLeftFieldException(field) + } + } + } + // For each field in left, descend with right + for((field, left_sub) <- left_r.elements) { + try { + right_r.elements.get(field) match { + case Some(right_sub) => connect(sourceInfo, connectCompileOptions, left_sub, right_sub, context_mod) + case None => { + if (connectCompileOptions.connectFieldsMustMatch) { + throw MissingRightFieldException(field) + } + } + } + } catch { + case BiConnectException(message) => throw BiConnectException(s".$field$message") + } + } + } + + + // These functions (finally) issue the connection operation + // Issue with right as sink, left as source + private def issueConnectL2R(left: Element, right: Element)(implicit sourceInfo: SourceInfo): Unit = { + // Source and sink are ambiguous in the case of a Bi/Bulk Connect (<>). + // If either is a DontCareBinding, just issue a DefInvalid for the other, + // otherwise, issue a Connect. + (left.topBinding, right.topBinding) match { + case (lb: DontCareBinding, _) => pushCommand(DefInvalid(sourceInfo, right.lref)) + case (_, rb: DontCareBinding) => pushCommand(DefInvalid(sourceInfo, left.lref)) + case (_, _) => pushCommand(Connect(sourceInfo, right.lref, left.ref)) + } + } + // Issue with left as sink, right as source + private def issueConnectR2L(left: Element, right: Element)(implicit sourceInfo: SourceInfo): Unit = { + // Source and sink are ambiguous in the case of a Bi/Bulk Connect (<>). + // If either is a DontCareBinding, just issue a DefInvalid for the other, + // otherwise, issue a Connect. + (left.topBinding, right.topBinding) match { + case (lb: DontCareBinding, _) => pushCommand(DefInvalid(sourceInfo, right.lref)) + case (_, rb: DontCareBinding) => pushCommand(DefInvalid(sourceInfo, left.lref)) + case (_, _) => pushCommand(Connect(sourceInfo, left.lref, right.ref)) + } + } + + // This function checks if element-level connection operation allowed. + // Then it either issues it or throws the appropriate exception. + def elemConnect(implicit sourceInfo: SourceInfo, connectCompileOptions: CompileOptions, left: Element, right: Element, context_mod: RawModule): Unit = { // scalastyle:ignore line.size.limit cyclomatic.complexity method.length + 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.topBinding.location.getOrElse(context_mod) + val right_mod: BaseModule = right.topBinding.location.getOrElse(context_mod) + + 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) && + (right_mod._parent.map(_ == context_mod).getOrElse(false)) ) { + // 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 (Input, Input) => issueConnectL2R(left, right) + case (Internal, Input) => issueConnectL2R(left, right) + + case (Output, Output) => issueConnectR2L(left, right) + case (Internal, Output) => issueConnectR2L(left, right) + + case (Input, Output) => throw BothDriversException + case (Output, Input) => throw NeitherDriverException + case (_, Internal) => throw UnknownRelationException + } + } + + // CASE: Context is same module as right node and left node is in child module + else if( (right_mod == context_mod) && + (left_mod._parent.map(_ == context_mod).getOrElse(false)) ) { + // 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 (Input, Input) => issueConnectR2L(left, right) + case (Input, Internal) => issueConnectR2L(left, right) + + case (Output, Output) => issueConnectL2R(left, right) + case (Output, Internal) => issueConnectL2R(left, right) + + case (Input, Output) => throw NeitherDriverException + case (Output, Input) => throw BothDriversException + case (Internal, _) => throw UnknownRelationException + } + } + + // CASE: Context is same module that both left node and right node are in + else if( (context_mod == left_mod) && (context_mod == right_mod) ) { + ((left_direction, right_direction): @unchecked) match { + // CURRENT MOD CURRENT MOD + case (Input, Output) => issueConnectL2R(left, right) + case (Input, Internal) => issueConnectL2R(left, right) + case (Internal, Output) => issueConnectL2R(left, right) + + case (Output, Input) => issueConnectR2L(left, right) + case (Output, Internal) => issueConnectR2L(left, right) + case (Internal, Input) => issueConnectR2L(left, right) + + case (Input, Input) => throw BothDriversException + case (Output, Output) => throw BothDriversException + case (Internal, Internal) => { + if (connectCompileOptions.dontAssumeDirectionality) { + throw UnknownDriverException + } else { + issueConnectR2L(left, right) + } + } + } + } + + // CASE: Context is the parent module of both the module containing left node + // and the module containing right node + // Note: This includes case when left and right in same module but in parent + else if( (left_mod._parent.map(_ == context_mod).getOrElse(false)) && + (right_mod._parent.map(_ == context_mod).getOrElse(false)) + ) { + // Thus both nodes must be ports and have a direction hint + ((left_direction, right_direction): @unchecked) match { + // CHILD MOD CHILD MOD + case (Input, Output) => issueConnectR2L(left, right) + case (Output, Input) => issueConnectL2R(left, right) + + case (Input, Input) => throw NeitherDriverException + case (Output, Output) => throw BothDriversException + case (_, Internal) => + if (connectCompileOptions.dontAssumeDirectionality) { + throw UnknownRelationException + } else { + issueConnectR2L(left, right) + } + case (Internal, _) => + if (connectCompileOptions.dontAssumeDirectionality) { + throw UnknownRelationException + } else { + issueConnectR2L(left, right) + } + } + } + + // Not quite sure where left and right are compared to current module + // so just error out + else throw UnknownRelationException + } + + // This function checks if analog element-level attaching is allowed, then marks the Analog as connected + def markAnalogConnected(implicit sourceInfo: SourceInfo, analog: Analog, contextModule: RawModule): Unit = { + analog.biConnectLocs.get(contextModule) match { + case Some(sl) => throw AttachAlreadyBulkConnectedException(sl) + case None => // Do nothing + } + // Mark bulk connected + analog.biConnectLocs(contextModule) = sourceInfo + } +} diff --git a/core/src/main/scala/chisel3/internal/Binding.scala b/core/src/main/scala/chisel3/internal/Binding.scala new file mode 100644 index 00000000..07c44f9f --- /dev/null +++ b/core/src/main/scala/chisel3/internal/Binding.scala @@ -0,0 +1,114 @@ +// See LICENSE for license details. + +package chisel3.internal + +import chisel3._ +import chisel3.experimental.BaseModule +import chisel3.internal.firrtl.LitArg + +/** Requires that a node is hardware ("bound") + */ +object requireIsHardware { + def apply(node: Data, msg: String = ""): Unit = { + node._parent match { // Compatibility layer hack + case Some(x: BaseModule) => x._compatAutoWrapPorts + case _ => + } + if (!node.isSynthesizable) { + val prefix = if (msg.nonEmpty) s"$msg " else "" + throw ExpectedHardwareException(s"$prefix'$node' must be hardware, " + + "not a bare Chisel type. Perhaps you forgot to wrap it in Wire(_) or IO(_)?") + } + } +} + +/** Requires that a node is a chisel type (not hardware, "unbound") + */ +object requireIsChiselType { + def apply(node: Data, msg: String = ""): Unit = if (node.isSynthesizable) { + val prefix = if (msg.nonEmpty) s"$msg " else "" + throw ExpectedChiselTypeException(s"$prefix'$node' must be a Chisel type, not hardware") + } +} + +// Element only direction used for the Binding system only. +private[chisel3] sealed abstract class BindingDirection +private[chisel3] 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): BindingDirection = { + 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'") + } + case _ => Internal + } + } +} + +// Location refers to 'where' in the Module hierarchy this lives +sealed trait Binding { + def location: Option[BaseModule] +} +// 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 TopBinding { + def location: Option[BaseModule] = None +} +// A constrained binding can only be read/written by specific modules +// Location will track where this Module is, and the bound object can be referenced in FIRRTL +sealed trait ConstrainedBinding extends TopBinding { + def enclosure: BaseModule + def location: Option[BaseModule] = { + // If an aspect is present, return the aspect module. Otherwise, return the enclosure module + // This allows aspect modules to pretend to be enclosed modules for connectivity checking, + // inside vs outside instance checking, etc. + Builder.aspectModule(enclosure) match { + case None => Some(enclosure) + case Some(aspect) => Some(aspect) + } + } +} + +// A binding representing a data that cannot be (re)assigned to. +sealed trait ReadOnlyBinding extends TopBinding + +// TODO(twigg): Ops between unenclosed nodes can also be unenclosed +// However, Chisel currently binds all op results to a module +case class OpBinding(enclosure: RawModule) extends ConstrainedBinding with ReadOnlyBinding +case class MemoryPortBinding(enclosure: RawModule) extends ConstrainedBinding +case class PortBinding(enclosure: BaseModule) extends ConstrainedBinding +case class RegBinding(enclosure: RawModule) extends ConstrainedBinding +case class WireBinding(enclosure: RawModule) extends ConstrainedBinding + +case class ChildBinding(parent: Data) extends Binding { + def location: Option[BaseModule] = parent.topBinding.location +} +/** Special binding for Vec.sample_element */ +case class SampleElementBinding[T <: Data](parent: Vec[T]) extends Binding { + def location = parent.topBinding.location +} +// A DontCare element has a specific Binding, somewhat like a literal. +// It is a source (RHS). It may only be connected/applied to sinks. +case class DontCareBinding() extends UnconstrainedBinding + +sealed trait LitBinding extends UnconstrainedBinding with ReadOnlyBinding +// Literal binding attached to a element that is not part of a Bundle. +case class ElementLitBinding(litArg: LitArg) extends LitBinding +// Literal binding attached to the root of a Bundle, containing literal values of its children. +case class BundleLitBinding(litMap: Map[Data, LitArg]) extends LitBinding diff --git a/core/src/main/scala/chisel3/internal/Builder.scala b/core/src/main/scala/chisel3/internal/Builder.scala new file mode 100644 index 00000000..773a9ad1 --- /dev/null +++ b/core/src/main/scala/chisel3/internal/Builder.scala @@ -0,0 +1,452 @@ +// See LICENSE for license details. + +package chisel3.internal + +import scala.util.DynamicVariable +import scala.collection.mutable.ArrayBuffer +import chisel3._ +import chisel3.experimental._ +import chisel3.internal.firrtl._ +import chisel3.internal.naming._ +import _root_.firrtl.annotations.{CircuitName, ComponentName, IsMember, ModuleName, Named, ReferenceTarget} + +import scala.collection.mutable + +private[chisel3] class Namespace(keywords: Set[String]) { + private val names = collection.mutable.HashMap[String, Long]() + for (keyword <- keywords) + names(keyword) = 1 + + private def rename(n: String): String = { + val index = names(n) + val tryName = s"${n}_${index}" + names(n) = index + 1 + if (this contains tryName) rename(n) else tryName + } + + private def sanitize(s: String, leadingDigitOk: Boolean = false): String = { + // TODO what character set does FIRRTL truly support? using ANSI C for now + def legalStart(c: Char) = (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' + def legal(c: Char) = legalStart(c) || (c >= '0' && c <= '9') + val res = s filter legal + val headOk = (!res.isEmpty) && (leadingDigitOk || legalStart(res.head)) + if (headOk) res else s"_$res" + } + + def contains(elem: String): Boolean = names.contains(elem) + + // leadingDigitOk is for use in fields of Records + def name(elem: String, leadingDigitOk: Boolean = false): String = { + val sanitized = sanitize(elem, leadingDigitOk) + if (this contains sanitized) { + name(rename(sanitized)) + } else { + names(sanitized) = 1 + sanitized + } + } +} + +private[chisel3] object Namespace { + /** Constructs an empty Namespace */ + def empty: Namespace = new Namespace(Set.empty[String]) +} + +private[chisel3] class IdGen { + private var counter = -1L + def next: Long = { + counter += 1 + counter + } +} + +/** Public API to access Node/Signal names. + * currently, the node's name, the full path name, and references to its parent Module and component. + * These are only valid once the design has been elaborated, and should not be used during its construction. + */ +trait InstanceId { + def instanceName: String + def pathName: String + def parentPathName: String + def parentModName: String + /** Returns a FIRRTL Named that refers to this object in the elaborated hardware graph */ + def toNamed: Named + /** Returns a FIRRTL IsMember that refers to this object in the elaborated hardware graph */ + def toTarget: IsMember + /** Returns a FIRRTL IsMember that refers to the absolute path to this object in the elaborated hardware graph */ + def toAbsoluteTarget: IsMember +} + +private[chisel3] trait HasId extends InstanceId { + private[chisel3] def _onModuleClose: Unit = {} // scalastyle:ignore method.name + private[chisel3] val _parent: Option[BaseModule] = Builder.currentModule + _parent.foreach(_.addId(this)) + + private[chisel3] val _id: Long = Builder.idGen.next + + // TODO: remove this, but its removal seems to cause a nasty Scala compiler crash. + override def hashCode: Int = super.hashCode() + override def equals(that: Any): Boolean = super.equals(that) + + // Facilities for 'suggesting' a name to this. + // Post-name hooks called to carry the suggestion to other candidates as needed + private var suggested_name: Option[String] = None + private val postname_hooks = scala.collection.mutable.ListBuffer.empty[String=>Unit] + // Only takes the first suggestion! + def suggestName(name: =>String): this.type = { + if(suggested_name.isEmpty) suggested_name = Some(name) + for(hook <- postname_hooks) { hook(name) } + this + } + private[chisel3] def suggestedName: Option[String] = suggested_name + private[chisel3] def addPostnameHook(hook: String=>Unit): Unit = postname_hooks += hook + + // Uses a namespace to convert suggestion into a true name + // Will not do any naming if the reference already assigned. + // (e.g. tried to suggest a name to part of a Record) + private[chisel3] def forceName(default: =>String, namespace: Namespace): Unit = + if(_ref.isEmpty) { + val candidate_name = suggested_name.getOrElse(default) + val available_name = namespace.name(candidate_name) + setRef(Ref(available_name)) + } + + private var _ref: Option[Arg] = None + private[chisel3] def setRef(imm: Arg): Unit = _ref = Some(imm) + private[chisel3] def setRef(parent: HasId, name: String): Unit = setRef(Slot(Node(parent), name)) + private[chisel3] def setRef(parent: HasId, index: Int): Unit = setRef(Index(Node(parent), ILit(index))) + private[chisel3] def setRef(parent: HasId, index: UInt): Unit = setRef(Index(Node(parent), index.ref)) + private[chisel3] def getRef: Arg = _ref.get + private[chisel3] def getOptionRef: Option[Arg] = _ref + + // Implementation of public methods. + def instanceName: String = _parent match { + case Some(p) => p._component match { + case Some(c) => _ref match { + case Some(arg) => arg fullName c + case None => suggested_name.getOrElse("??") + } + case None => throwException("signalName/pathName should be called after circuit elaboration") + } + case None => throwException("this cannot happen") + } + def pathName: String = _parent match { + case None => instanceName + case Some(p) => s"${p.pathName}.$instanceName" + } + def parentPathName: String = _parent match { + case Some(p) => p.pathName + case None => throwException(s"$instanceName doesn't have a parent") + } + def parentModName: String = _parent match { + case Some(p) => p.name + case None => throwException(s"$instanceName doesn't have a parent") + } + // TODO Should this be public? + protected def circuitName: String = _parent match { + case None => instanceName + case Some(p) => p.circuitName + } + + private[chisel3] def getPublicFields(rootClass: Class[_]): Seq[java.lang.reflect.Method] = { + // Suggest names to nodes using runtime reflection + def getValNames(c: Class[_]): Set[String] = { + if (c == rootClass) { + Set() + } else { + getValNames(c.getSuperclass) ++ c.getDeclaredFields.map(_.getName) + } + } + val valNames = getValNames(this.getClass) + def isPublicVal(m: java.lang.reflect.Method) = + m.getParameterTypes.isEmpty && valNames.contains(m.getName) && !m.getDeclaringClass.isAssignableFrom(rootClass) + this.getClass.getMethods.sortWith(_.getName < _.getName).filter(isPublicVal(_)) + } +} +/** Holds the implementation of toNamed for Data and MemBase */ +private[chisel3] trait NamedComponent extends HasId { + /** Returns a FIRRTL ComponentName that references this object + * @note Should not be called until circuit elaboration is complete + */ + final def toNamed: ComponentName = + ComponentName(this.instanceName, ModuleName(this.parentModName, CircuitName(this.circuitName))) + + /** Returns a FIRRTL ReferenceTarget that references this object + * @note Should not be called until circuit elaboration is complete + */ + final def toTarget: ReferenceTarget = { + val name = this.instanceName + import _root_.firrtl.annotations.{Target, TargetToken} + Target.toTargetTokens(name).toList match { + case TargetToken.Ref(r) :: components => ReferenceTarget(this.circuitName, this.parentModName, Nil, r, components) + case other => + throw _root_.firrtl.annotations.Target.NamedException(s"Cannot convert $name into [[ReferenceTarget]]: $other") + } + } + + final def toAbsoluteTarget: ReferenceTarget = { + val localTarget = toTarget + _parent match { + case Some(parent) => parent.toAbsoluteTarget.ref(localTarget.ref).copy(component = localTarget.component) + case None => localTarget + } + } +} + +// Mutable global state for chisel that can appear outside a Builder context +private[chisel3] class ChiselContext() { + val idGen = new IdGen + + // Record the Bundle instance, class name, method name, and reverse stack trace position of open Bundles + val bundleStack: ArrayBuffer[(Bundle, String, String, Int)] = ArrayBuffer() +} + +private[chisel3] class DynamicContext() { + val globalNamespace = Namespace.empty + val components = ArrayBuffer[Component]() + val annotations = ArrayBuffer[ChiselAnnotation]() + var currentModule: Option[BaseModule] = None + + /** Contains a mapping from a elaborated module to their aspect + * Set by [[ModuleAspect]] + */ + val aspectModule: mutable.HashMap[BaseModule, BaseModule] = mutable.HashMap.empty[BaseModule, BaseModule] + + // Set by object Module.apply before calling class Module constructor + // Used to distinguish between no Module() wrapping, multiple wrappings, and rewrapping + var readyForModuleConstr: Boolean = false + var whenDepth: Int = 0 // Depth of when nesting + var currentClock: Option[Clock] = None + var currentReset: Option[Reset] = None + val errors = new ErrorLog + val namingStack = new NamingStack +} + +//scalastyle:off number.of.methods +private[chisel3] object Builder { + // All global mutable state must be referenced via dynamicContextVar!! + private val dynamicContextVar = new DynamicVariable[Option[DynamicContext]](None) + private def dynamicContext: DynamicContext = { + require(dynamicContextVar.value.isDefined, "must be inside Builder context") + dynamicContextVar.value.get + } + + private val chiselContext = new DynamicVariable[ChiselContext](new ChiselContext) + + // Initialize any singleton objects before user code inadvertently inherits them. + private def initializeSingletons(): Unit = { + // This used to contain: + // val dummy = core.DontCare + // but this would occasionally produce hangs due to static initialization deadlock + // when Builder initialization collided with chisel3.package initialization of the DontCare value. + // See: + // http://ternarysearch.blogspot.com/2013/07/static-initialization-deadlock.html + // https://bugs.openjdk.java.net/browse/JDK-8037567 + // https://stackoverflow.com/questions/28631656/runnable-thread-state-but-in-object-wait + } + + def namingStackOption: Option[NamingStack] = dynamicContextVar.value.map(_.namingStack) + + def idGen: IdGen = chiselContext.value.idGen + + def globalNamespace: Namespace = dynamicContext.globalNamespace + def components: ArrayBuffer[Component] = dynamicContext.components + def annotations: ArrayBuffer[ChiselAnnotation] = dynamicContext.annotations + def namingStack: NamingStack = dynamicContext.namingStack + + def currentModule: Option[BaseModule] = dynamicContextVar.value match { + case Some(dyanmicContext) => dynamicContext.currentModule + case _ => None + } + def currentModule_=(target: Option[BaseModule]): Unit = { + dynamicContext.currentModule = target + } + def aspectModule(module: BaseModule): Option[BaseModule] = dynamicContextVar.value match { + case Some(dynamicContext) => dynamicContext.aspectModule.get(module) + case _ => None + } + def addAspect(module: BaseModule, aspect: BaseModule): Unit = { + dynamicContext.aspectModule += ((module, aspect)) + } + def forcedModule: BaseModule = currentModule match { + case Some(module) => module + case None => throwException( + "Error: Not in a Module. Likely cause: Missed Module() wrap or bare chisel API call." + // A bare api call is, e.g. calling Wire() from the scala console). + ) + } + def referenceUserModule: RawModule = { + currentModule match { + case Some(module: RawModule) => + aspectModule(module) match { + case Some(aspect: RawModule) => aspect + case other => module + } + case _ => throwException( + "Error: Not in a RawModule. Likely cause: Missed Module() wrap, bare chisel API call, or attempting to construct hardware inside a BlackBox." // scalastyle:ignore line.size.limit + // A bare api call is, e.g. calling Wire() from the scala console). + ) + } + } + def forcedUserModule: RawModule = currentModule match { + case Some(module: RawModule) => module + case _ => throwException( + "Error: Not in a UserModule. Likely cause: Missed Module() wrap, bare chisel API call, or attempting to construct hardware inside a BlackBox." // scalastyle:ignore line.size.limit + // A bare api call is, e.g. calling Wire() from the scala console). + ) + } + def readyForModuleConstr: Boolean = dynamicContext.readyForModuleConstr + def readyForModuleConstr_=(target: Boolean): Unit = { + dynamicContext.readyForModuleConstr = target + } + def whenDepth: Int = dynamicContext.whenDepth + def whenDepth_=(target: Int): Unit = { + dynamicContext.whenDepth = target + } + def currentClock: Option[Clock] = dynamicContext.currentClock + def currentClock_=(newClock: Option[Clock]): Unit = { + dynamicContext.currentClock = newClock + } + + def currentReset: Option[Reset] = dynamicContext.currentReset + def currentReset_=(newReset: Option[Reset]): Unit = { + dynamicContext.currentReset = newReset + } + + def forcedClock: Clock = currentClock.getOrElse( + throwException("Error: No implicit clock.") + ) + def forcedReset: Reset = currentReset.getOrElse( + throwException("Error: No implicit reset.") + ) + + // TODO(twigg): Ideally, binding checks and new bindings would all occur here + // However, rest of frontend can't support this yet. + def pushCommand[T <: Command](c: T): T = { + forcedUserModule.addCommand(c) + c + } + def pushOp[T <: Data](cmd: DefPrim[T]): T = { + // Bind each element of the returned Data to being a Op + cmd.id.bind(OpBinding(forcedUserModule)) + pushCommand(cmd).id + } + + // Called when Bundle construction begins, used to record a stack of open Bundle constructors to + // record candidates for Bundle autoclonetype. This is a best-effort guess. + // Returns the current stack of open Bundles + // Note: elt will NOT have finished construction, its elements cannot be accessed + def updateBundleStack(elt: Bundle): Seq[Bundle] = { + val stackElts = Thread.currentThread().getStackTrace() + .reverse // so stack frame numbers are deterministic across calls + .dropRight(2) // discard Thread.getStackTrace and updateBundleStack + + // Determine where we are in the Bundle stack + val eltClassName = elt.getClass.getName + val eltStackPos = stackElts.map(_.getClassName).lastIndexOf(eltClassName) + + // Prune the existing Bundle stack of closed Bundles + // If we know where we are in the stack, discard frames above that + val stackEltsTop = if (eltStackPos >= 0) eltStackPos else stackElts.size + val pruneLength = chiselContext.value.bundleStack.reverse.prefixLength { case (_, cname, mname, pos) => + pos >= stackEltsTop || stackElts(pos).getClassName != cname || stackElts(pos).getMethodName != mname + } + chiselContext.value.bundleStack.trimEnd(pruneLength) + + // Return the stack state before adding the most recent bundle + val lastStack = chiselContext.value.bundleStack.map(_._1).toSeq + + // Append the current Bundle to the stack, if it's on the stack trace + if (eltStackPos >= 0) { + val stackElt = stackElts(eltStackPos) + chiselContext.value.bundleStack.append((elt, eltClassName, stackElt.getMethodName, eltStackPos)) + } + // Otherwise discard the stack frame, this shouldn't fail noisily + + lastStack + } + + /** Recursively suggests names to supported "container" classes + * Arbitrary nestings of supported classes are allowed so long as the + * innermost element is of type HasId + * (Note: Map is Iterable[Tuple2[_,_]] and thus excluded) + */ + def nameRecursively(prefix: String, nameMe: Any, namer: (HasId, String) => Unit): Unit = nameMe match { + case (id: HasId) => namer(id, prefix) + case Some(elt) => nameRecursively(prefix, elt, namer) + case (iter: Iterable[_]) if iter.hasDefiniteSize => + for ((elt, i) <- iter.zipWithIndex) { + nameRecursively(s"${prefix}_${i}", elt, namer) + } + case _ => // Do nothing + } + + def errors: ErrorLog = dynamicContext.errors + def error(m: => String): Unit = if (dynamicContextVar.value.isDefined) errors.error(m) + def warning(m: => String): Unit = if (dynamicContextVar.value.isDefined) errors.warning(m) + def deprecated(m: => String, location: Option[String] = None): Unit = + if (dynamicContextVar.value.isDefined) errors.deprecated(m, location) + + /** Record an exception as an error, and throw it. + * + * @param m exception message + */ + @throws(classOf[ChiselException]) + def exception(m: => String): Unit = { + error(m) + throwException(m) + } + + def build[T <: RawModule](f: => T): (Circuit, T) = { + chiselContext.withValue(new ChiselContext) { + dynamicContextVar.withValue(Some(new DynamicContext())) { + errors.info("Elaborating design...") + val mod = f + mod.forceName(mod.name, globalNamespace) + errors.checkpoint() + errors.info("Done elaborating.") + + (Circuit(components.last.name, components, annotations), mod) + } + } + } + initializeSingletons() +} + +/** Allows public access to the naming stack in Builder / DynamicContext, and handles invocations + * outside a Builder context. + * Necessary because naming macros expand in user code and don't have access into private[chisel3] + * objects. + */ +object DynamicNamingStack { + def pushContext(): NamingContextInterface = { + Builder.namingStackOption match { + case Some(namingStack) => namingStack.pushContext() + case None => DummyNamer + } + } + + def popReturnContext[T <: Any](prefixRef: T, until: NamingContextInterface): T = { + until match { + case DummyNamer => + require(Builder.namingStackOption.isEmpty, + "Builder context must remain stable throughout a chiselName-annotated function invocation") + case context: NamingContext => + require(Builder.namingStackOption.isDefined, + "Builder context must remain stable throughout a chiselName-annotated function invocation") + Builder.namingStackOption.get.popContext(prefixRef, context) + } + prefixRef + } + + def length() : Int = Builder.namingStackOption.get.length +} + +/** Casts BigInt to Int, issuing an error when the input isn't representable. */ +private[chisel3] object castToInt { + def apply(x: BigInt, msg: String): Int = { + val res = x.toInt + require(x == res, s"$msg $x is too large to be represented as Int") + res + } +} diff --git a/core/src/main/scala/chisel3/internal/Error.scala b/core/src/main/scala/chisel3/internal/Error.scala new file mode 100644 index 00000000..369da52e --- /dev/null +++ b/core/src/main/scala/chisel3/internal/Error.scala @@ -0,0 +1,213 @@ +// See LICENSE for license details. + +package chisel3.internal + +import scala.annotation.tailrec +import scala.collection.mutable.{ArrayBuffer, LinkedHashMap} + +class ChiselException(message: String, cause: Throwable = null) extends Exception(message, cause) { + + /** Package names whose stack trace elements should be trimmed when generating a trimmed stack trace */ + val blacklistPackages: Set[String] = Set("chisel3", "scala", "java", "sun", "sbt") + + /** The object name of Chisel's internal `Builder`. Everything stack trace element after this will be trimmed. */ + val builderName: String = chisel3.internal.Builder.getClass.getName + + /** Examine a [[Throwable]], to extract all its causes. Innermost cause is first. + * @param throwable an exception to examine + * @return a sequence of all the causes with innermost cause first + */ + @tailrec + private def getCauses(throwable: Throwable, acc: Seq[Throwable] = Seq.empty): Seq[Throwable] = + throwable.getCause() match { + case null => throwable +: acc + case a => getCauses(a, throwable +: acc) + } + + /** Returns true if an exception contains */ + private def containsBuilder(throwable: Throwable): Boolean = + throwable.getStackTrace().collectFirst { + case ste if ste.getClassName().startsWith(builderName) => throwable + }.isDefined + + /** Examine this [[ChiselException]] and it's causes for the first [[Throwable]] that contains a stack trace including + * a stack trace element whose declaring class is the [[builderName]]. If no such element exists, return this + * [[ChiselException]]. + */ + private lazy val likelyCause: Throwable = + getCauses(this).collectFirst{ case a if containsBuilder(a) => a }.getOrElse(this) + + /** For an exception, return a stack trace trimmed to user code only + * + * This does the following actions: + * + * 1. Trims the top of the stack trace while elements match [[blacklistPackages]] + * 2. Trims the bottom of the stack trace until an element matches [[builderName]] + * 3. Trims from the [[builderName]] all [[blacklistPackages]] + * + * @param throwable the exception whose stack trace should be trimmed + * @return an array of stack trace elements + */ + private def trimmedStackTrace(throwable: Throwable): Array[StackTraceElement] = { + def isBlacklisted(ste: StackTraceElement) = { + val packageName = ste.getClassName().takeWhile(_ != '.') + blacklistPackages.contains(packageName) + } + + val trimmedLeft = throwable.getStackTrace().view.dropWhile(isBlacklisted) + val trimmedReverse = trimmedLeft.reverse + .dropWhile(ste => !ste.getClassName.startsWith(builderName)) + .dropWhile(isBlacklisted) + trimmedReverse.reverse.toArray + } + + /** trims the top of the stack of elements belonging to [[blacklistPackages]] + * then trims the bottom elements until it reaches [[builderName]] + * then continues trimming elements belonging to [[blacklistPackages]] + */ + @deprecated("This method will be removed in 3.4", "3.3") + def trimmedStackTrace: Array[StackTraceElement] = trimmedStackTrace(this) + + def chiselStackTrace: String = { + val trimmed = trimmedStackTrace(likelyCause) + + val sw = new java.io.StringWriter + sw.write(likelyCause.toString + "\n") + sw.write("\t...\n") + trimmed.foreach(ste => sw.write(s"\tat $ste\n")) + sw.write("\t... (Stack trace trimmed to user code only, rerun with --full-stacktrace if you wish to see the full stack trace)\n") // scalastyle:ignore line.size.limit + sw.toString + } +} + +private[chisel3] object throwException { + def apply(s: String, t: Throwable = null): Nothing = + throw new ChiselException(s, t) +} + +/** Records and reports runtime errors and warnings. */ +private[chisel3] object ErrorLog { + val depTag = s"[${Console.BLUE}deprecated${Console.RESET}]" + val warnTag = s"[${Console.YELLOW}warn${Console.RESET}]" + val errTag = s"[${Console.RED}error${Console.RESET}]" +} + +private[chisel3] class ErrorLog { + /** Log an error message */ + def error(m: => String): Unit = + errors += new Error(m, getUserLineNumber) + + /** Log a warning message */ + def warning(m: => String): Unit = + errors += new Warning(m, getUserLineNumber) + + /** Emit an informational message */ + def info(m: String): Unit = + println(new Info("[%2.3f] %s".format(elapsedTime/1e3, m), None)) // scalastyle:ignore regex + + /** Log a deprecation warning message */ + def deprecated(m: => String, location: Option[String]): Unit = { + val sourceLoc = location match { + case Some(loc) => loc + case None => getUserLineNumber match { + case Some(elt: StackTraceElement) => s"${elt.getFileName}:${elt.getLineNumber}" + case None => "(unknown)" + } + } + + val thisEntry = (m, sourceLoc) + deprecations += ((thisEntry, deprecations.getOrElse(thisEntry, 0) + 1)) + } + + /** Throw an exception if any errors have yet occurred. */ + def checkpoint(): Unit = { + // scalastyle:off line.size.limit regex + deprecations.foreach { case ((message, sourceLoc), count) => + println(s"${ErrorLog.depTag} $sourceLoc ($count calls): $message") + } + errors foreach println + + if (!deprecations.isEmpty) { + println(s"${ErrorLog.warnTag} ${Console.YELLOW}There were ${deprecations.size} deprecated function(s) used." + + s" These may stop compiling in a future release - you are encouraged to fix these issues.${Console.RESET}") + println(s"${ErrorLog.warnTag} Line numbers for deprecations reported by Chisel may be inaccurate; enable scalac compiler deprecation warnings via either of the following methods:") + println(s"${ErrorLog.warnTag} In the sbt interactive console, enter:") + println(s"""${ErrorLog.warnTag} set scalacOptions in ThisBuild ++= Seq("-unchecked", "-deprecation")""") + println(s"${ErrorLog.warnTag} or, in your build.sbt, add the line:") + println(s"""${ErrorLog.warnTag} scalacOptions := Seq("-unchecked", "-deprecation")""") + } + + val allErrors = errors.filter(_.isFatal) + val allWarnings = errors.filter(!_.isFatal) + + if (!allWarnings.isEmpty && !allErrors.isEmpty) { + println(s"${ErrorLog.errTag} There were ${Console.RED}${allErrors.size} error(s)${Console.RESET} and ${Console.YELLOW}${allWarnings.size} warning(s)${Console.RESET} during hardware elaboration.") + } else if (!allWarnings.isEmpty) { + println(s"${ErrorLog.warnTag} There were ${Console.YELLOW}${allWarnings.size} warning(s)${Console.RESET} during hardware elaboration.") + } else if (!allErrors.isEmpty) { + println(s"${ErrorLog.errTag} There were ${Console.RED}${allErrors.size} error(s)${Console.RESET} during hardware elaboration.") + } + + if (!allErrors.isEmpty) { + throwException("Fatal errors during hardware elaboration") + } else { + // No fatal errors, clear accumulated warnings since they've been reported + errors.clear() + } + // scalastyle:on line.size.limit regex + } + + /** Returns the best guess at the first stack frame that belongs to user code. + */ + private def getUserLineNumber = { + def isChiselClassname(className: String): Boolean = { + // List of classpath prefixes that are Chisel internals and should be ignored when looking for user code + // utils are not part of internals and errors there can be reported + val chiselPrefixes = Set( + "java.", + "scala.", + "chisel3.internal.", + "chisel3.experimental.", + "chisel3.package$" // for some compatibility / deprecated types + ) + !chiselPrefixes.filter(className.startsWith(_)).isEmpty + } + + Thread.currentThread().getStackTrace.toList.dropWhile( + // Get rid of everything in Chisel core + ste => isChiselClassname(ste.getClassName) + ).headOption + } + + private val errors = ArrayBuffer[LogEntry]() + private val deprecations = LinkedHashMap[(String, String), Int]() + + private val startTime = System.currentTimeMillis + private def elapsedTime: Long = System.currentTimeMillis - startTime +} + +private abstract class LogEntry(msg: => String, line: Option[StackTraceElement]) { + def isFatal: Boolean = false + def format: String + + override def toString: String = line match { + case Some(l) => s"${format} ${l.getFileName}:${l.getLineNumber}: ${msg} in class ${l.getClassName}" + case None => s"${format} ${msg}" + } + + protected def tag(name: String, color: String): String = + s"[${color}${name}${Console.RESET}]" +} + +private class Error(msg: => String, line: Option[StackTraceElement]) extends LogEntry(msg, line) { + override def isFatal: Boolean = true + def format: String = tag("error", Console.RED) +} + +private class Warning(msg: => String, line: Option[StackTraceElement]) extends LogEntry(msg, line) { + def format: String = tag("warn", Console.YELLOW) +} + +private class Info(msg: => String, line: Option[StackTraceElement]) extends LogEntry(msg, line) { + def format: String = tag("info", Console.MAGENTA) +} diff --git a/core/src/main/scala/chisel3/internal/MonoConnect.scala b/core/src/main/scala/chisel3/internal/MonoConnect.scala new file mode 100644 index 00000000..41402021 --- /dev/null +++ b/core/src/main/scala/chisel3/internal/MonoConnect.scala @@ -0,0 +1,264 @@ +// See LICENSE for license details. + +package chisel3.internal + +import chisel3._ +import chisel3.experimental.{Analog, BaseModule, EnumType, FixedPoint, Interval, UnsafeEnum} +import chisel3.internal.Builder.pushCommand +import chisel3.internal.firrtl.{Connect, DefInvalid} +import scala.language.experimental.macros +import chisel3.internal.sourceinfo.SourceInfo + +/** +* MonoConnect.connect executes a mono-directional connection element-wise. +* +* Note that this isn't commutative. There is an explicit source and sink +* already determined before this function is called. +* +* The connect operation will recurse down the left Data (with the right Data). +* An exception will be thrown if a movement through the left cannot be matched +* in the right. The right side is allowed to have extra Record fields. +* Vecs must still be exactly the same size. +* +* See elemConnect for details on how the root connections are issued. +* +* Note that a valid sink must be writable so, one of these must hold: +* - Is an internal writable node (Reg or Wire) +* - Is an output of the current module +* - Is an input of a submodule of the current module +* +* Note that a valid source must be readable so, one of these must hold: +* - Is an internal readable node (Reg, Wire, Op) +* - Is a literal +* - Is a port of the current module or submodule of the current module +*/ + +private[chisel3] object MonoConnect { + // scalastyle:off method.name public.methods.have.type + // These are all the possible exceptions that can be thrown. + // These are from element-level connection + def UnreadableSourceException = + MonoConnectException(": Source is unreadable from current module.") + def UnwritableSinkException = + MonoConnectException(": Sink is unwriteable by current module.") + def UnknownRelationException = + MonoConnectException(": Sink or source unavailable to current module.") + // These are when recursing down aggregate types + def MismatchedVecException = + MonoConnectException(": Sink and Source are different length Vecs.") + def MissingFieldException(field: String) = + MonoConnectException(s": Source Record missing field ($field).") + def MismatchedException(sink: String, source: String) = + MonoConnectException(s": Sink ($sink) and Source ($source) have different types.") + def DontCareCantBeSink = + MonoConnectException(": DontCare cannot be a connection sink (LHS)") + def AnalogCantBeMonoSink = + MonoConnectException(": Analog cannot participate in a mono connection (sink - LHS)") + def AnalogCantBeMonoSource = + MonoConnectException(": Analog cannot participate in a mono connection (source - RHS)") + def AnalogMonoConnectionException = + MonoConnectException(": Analog cannot participate in a mono connection (source and sink)") + // scalastyle:on method.name public.methods.have.type + + /** This function is what recursively tries to connect a sink and source together + * + * There is some cleverness in the use of internal try-catch to catch exceptions + * during the recursive decent and then rethrow them with extra information added. + * This gives the user a 'path' to where in the connections things went wrong. + */ + def connect( //scalastyle:off cyclomatic.complexity method.length + sourceInfo: SourceInfo, + connectCompileOptions: CompileOptions, + sink: Data, + source: Data, + context_mod: RawModule): Unit = + (sink, source) match { + + // Handle legal element cases, note (Bool, Bool) is caught by the first two, as Bool is a UInt + case (sink_e: Bool, source_e: UInt) => + elemConnect(sourceInfo, connectCompileOptions, sink_e, source_e, context_mod) + case (sink_e: UInt, source_e: Bool) => + elemConnect(sourceInfo, connectCompileOptions, sink_e, source_e, context_mod) + case (sink_e: UInt, source_e: UInt) => + elemConnect(sourceInfo, connectCompileOptions, sink_e, source_e, context_mod) + case (sink_e: SInt, source_e: SInt) => + elemConnect(sourceInfo, connectCompileOptions, sink_e, source_e, context_mod) + case (sink_e: FixedPoint, source_e: FixedPoint) => + elemConnect(sourceInfo, connectCompileOptions, sink_e, source_e, context_mod) + case (sink_e: Interval, source_e: Interval) => + elemConnect(sourceInfo, connectCompileOptions, sink_e, source_e, context_mod) + case (sink_e: Clock, source_e: Clock) => + elemConnect(sourceInfo, connectCompileOptions, sink_e, source_e, context_mod) + case (sink_e: AsyncReset, source_e: AsyncReset) => + elemConnect(sourceInfo, connectCompileOptions, sink_e, source_e, context_mod) + case (sink_e: ResetType, source_e: Reset) => + elemConnect(sourceInfo, connectCompileOptions, sink_e, source_e, context_mod) + case (sink_e: EnumType, source_e: UnsafeEnum) => + elemConnect(sourceInfo, connectCompileOptions, sink_e, source_e, context_mod) + case (sink_e: EnumType, source_e: EnumType) if sink_e.typeEquivalent(source_e) => + elemConnect(sourceInfo, connectCompileOptions, sink_e, source_e, context_mod) + case (sink_e: UnsafeEnum, source_e: UInt) => + elemConnect(sourceInfo, connectCompileOptions, sink_e, source_e, context_mod) + + // Handle Vec case + case (sink_v: Vec[Data @unchecked], source_v: Vec[Data @unchecked]) => + if(sink_v.length != source_v.length) { throw MismatchedVecException } + for(idx <- 0 until sink_v.length) { + try { + implicit val compileOptions = connectCompileOptions + connect(sourceInfo, connectCompileOptions, sink_v(idx), source_v(idx), context_mod) + } catch { + case MonoConnectException(message) => throw MonoConnectException(s"($idx)$message") + } + } + // Handle Vec connected to DontCare. Apply the DontCare to individual elements. + case (sink_v: Vec[Data @unchecked], DontCare) => + for(idx <- 0 until sink_v.length) { + try { + implicit val compileOptions = connectCompileOptions + connect(sourceInfo, connectCompileOptions, sink_v(idx), source, context_mod) + } catch { + case MonoConnectException(message) => throw MonoConnectException(s"($idx)$message") + } + } + + // Handle Record case + case (sink_r: Record, source_r: Record) => + // For each field, descend with right + for((field, sink_sub) <- sink_r.elements) { + try { + source_r.elements.get(field) match { + case Some(source_sub) => connect(sourceInfo, connectCompileOptions, sink_sub, source_sub, context_mod) + case None => { + if (connectCompileOptions.connectFieldsMustMatch) { + throw MissingFieldException(field) + } + } + } + } catch { + case MonoConnectException(message) => throw MonoConnectException(s".$field$message") + } + } + // Handle Record connected to DontCare. Apply the DontCare to individual elements. + case (sink_r: Record, DontCare) => + // For each field, descend with right + for((field, sink_sub) <- sink_r.elements) { + try { + connect(sourceInfo, connectCompileOptions, sink_sub, source, context_mod) + } catch { + case MonoConnectException(message) => throw MonoConnectException(s".$field$message") + } + } + + // Source is DontCare - it may be connected to anything. It generates a defInvalid for the sink. + case (sink, DontCare) => pushCommand(DefInvalid(sourceInfo, sink.lref)) + // DontCare as a sink is illegal. + case (DontCare, _) => throw DontCareCantBeSink + // Analog is illegal in mono connections. + case (_: Analog, _:Analog) => throw AnalogMonoConnectionException + // Analog is illegal in mono connections. + case (_: Analog, _) => throw AnalogCantBeMonoSink + // Analog is illegal in mono connections. + case (_, _: Analog) => throw AnalogCantBeMonoSource + // Sink and source are different subtypes of data so fail + case (sink, source) => throw MismatchedException(sink.toString, source.toString) + } + + // This function (finally) issues the connection operation + private def issueConnect(sink: Element, source: Element)(implicit sourceInfo: SourceInfo): Unit = { + // If the source is a DontCare, generate a DefInvalid for the sink, + // otherwise, issue a Connect. + source.topBinding match { + case b: DontCareBinding => pushCommand(DefInvalid(sourceInfo, sink.lref)) + case _ => pushCommand(Connect(sourceInfo, sink.lref, source.ref)) + } + } + + // This function checks if element-level connection operation allowed. + // Then it either issues it or throws the appropriate exception. + def elemConnect(implicit sourceInfo: SourceInfo, connectCompileOptions: CompileOptions, sink: Element, source: Element, context_mod: RawModule): Unit = { // scalastyle:ignore line.size.limit + 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.topBinding.location.getOrElse(throw UnwritableSinkException) + val source_mod: BaseModule = source.topBinding.location.getOrElse(context_mod) + + 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 (Output, _) => issueConnect(sink, source) + case (Internal, _) => issueConnect(sink, source) + case (Input, _) => throw UnwritableSinkException + } + } + + // CASE: Context is same module as sink node and right node is in a child module + else if( (sink_mod == context_mod) && + (source_mod._parent.map(_ == context_mod).getOrElse(false)) ) { + // Thus, right node better be a port node and thus have a direction + ((sink_direction, source_direction): @unchecked) match { + // SINK SOURCE + // CURRENT MOD CHILD MOD + 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 (Input, Output) if (!(connectCompileOptions.dontTryConnectionsSwapped)) => issueConnect(source, sink) // scalastyle:ignore line.size.limit + case (Input, _) => throw UnwritableSinkException + } + } + + // CASE: Context is same module as source node and sink node is in child module + else if( (source_mod == context_mod) && + (sink_mod._parent.map(_ == context_mod).getOrElse(false)) ) { + // Thus, left node better be a port node and thus have a direction + ((sink_direction, source_direction): @unchecked) match { + // SINK SOURCE + // CHILD MOD CURRENT MOD + case (Input, _) => issueConnect(sink, source) + case (Output, _) => throw UnwritableSinkException + case (Internal, _) => throw UnwritableSinkException + } + } + + // CASE: Context is the parent module of both the module containing sink node + // and the module containing source node + // Note: This includes case when sink and source in same module but in parent + else if( (sink_mod._parent.map(_ == context_mod).getOrElse(false)) && + (source_mod._parent.map(_ == context_mod).getOrElse(false)) + ) { + // Thus both nodes must be ports and have a direction + ((sink_direction, source_direction): @unchecked) match { + // SINK SOURCE + // CHILD MOD CHILD MOD + 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 (Internal, _) => throw UnwritableSinkException + } + } + + // Not quite sure where left and right are compared to current module + // so just error out + else throw UnknownRelationException + } +} diff --git a/core/src/main/scala/chisel3/internal/Namer.scala b/core/src/main/scala/chisel3/internal/Namer.scala new file mode 100644 index 00000000..999971a4 --- /dev/null +++ b/core/src/main/scala/chisel3/internal/Namer.scala @@ -0,0 +1,154 @@ +// See LICENSE for license details. + +// This file contains part of the implementation of the naming static annotation system. + +package chisel3.internal.naming +import chisel3.experimental.NoChiselNamePrefix + +import scala.collection.mutable.Stack +import scala.collection.mutable.ListBuffer + +import scala.collection.JavaConversions._ + +import java.util.IdentityHashMap + +/** Recursive Function Namer overview + * + * In every function, creates a NamingContext object, which associates all vals with a string name + * suffix, for example: + * val myValName = SomeStatement() + * produces the entry in items: + * {ref of SomeStatement(), "myValName"} + * + * This is achieved with a macro transforming: + * val myValName = SomeStatement() + * statements into a naming call: + * val myValName = context.name(SomeStatement(), "myValName") + * + * The context is created from a global dynamic context stack at the beginning of each function. + * At the end of each function call, the completed context is added to its parent context and + * associated with the return value (whose name at an enclosing function call will form the prefix + * for all named objects). + * + * When the naming context prefix is given, it will name all of its items with the prefix and the + * associated suffix name. Then, it will check its descendants for sub-contexts with references + * matching the item reference, and if there is a match, it will (recursively) give the + * sub-context a prefix of its current prefix plus the item reference suffix. + * + * Note that for Modules, the macro will insert a naming context prefix call with an empty prefix, + * starting the recursive naming process. + */ + +/** Base class for naming contexts, providing the basic API consisting of naming calls and + * ability to take descendant naming contexts. + */ +sealed trait NamingContextInterface { + /** Suggest a name (that will be propagated to FIRRTL) for an object, then returns the object + * itself (so this can be inserted transparently anywhere). + * Is a no-op (so safe) when applied on objects that aren't named, including non-Chisel data + * types. + */ + def name[T](obj: T, name: String): T + + /** Gives this context a naming prefix (which may be empty, "", for a top-level Module context) + * so that actual naming calls (HasId.suggestName) can happen. + * Recursively names descendants, for those whose return value have an associated name. + */ + def namePrefix(prefix: String) +} + +/** Dummy implementation to allow for naming annotations in a non-Builder context. + */ +object DummyNamer extends NamingContextInterface { + def name[T](obj: T, name: String): T = obj + + def namePrefix(prefix: String): Unit = { + } +} + +/** Actual namer functionality. + */ +class NamingContext extends NamingContextInterface { + val descendants = new IdentityHashMap[AnyRef, ListBuffer[NamingContext]]() + val anonymousDescendants = ListBuffer[NamingContext]() + val items = ListBuffer[(AnyRef, String)]() + var closed = false // a sanity check to ensure no more name() calls are done after name_prefix + + /** Adds a NamingContext object as a descendant - where its contained objects will have names + * prefixed with the name given to the reference object, if the reference object is named in the + * scope of this context. + */ + def addDescendant(ref: Any, descendant: NamingContext) { + ref match { + case ref: AnyRef => + descendants.getOrElseUpdate(ref, ListBuffer[NamingContext]()) += descendant + case _ => anonymousDescendants += descendant + } + } + + def name[T](obj: T, name: String): T = { + assert(!closed, "Can't name elements after name_prefix called") + obj match { + case _: NoChiselNamePrefix => // Don't name things with NoChiselNamePrefix + case ref: AnyRef => items += ((ref, name)) + case _ => + } + obj + } + + def namePrefix(prefix: String): Unit = { + closed = true + for ((ref, suffix) <- items) { + // First name the top-level object + chisel3.internal.Builder.nameRecursively(prefix + suffix, ref, (id, name) => id.suggestName(name)) + + // Then recurse into descendant contexts + if (descendants.containsKey(ref)) { + for (descendant <- descendants.get(ref)) { + descendant.namePrefix(prefix + suffix + "_") + } + descendants.remove(ref) + } + } + + for (descendant <- descendants.values().flatten) { + // Where we have a broken naming link, just ignore the missing parts + descendant.namePrefix(prefix) + } + for (descendant <- anonymousDescendants) { + descendant.namePrefix(prefix) + } + } +} + +/** Class for the (global) naming stack object, which provides a way to push and pop naming + * contexts as functions are called / finished. + */ +class NamingStack { + val namingStack = Stack[NamingContext]() + + /** Creates a new naming context, where all items in the context will have their names prefixed + * with some yet-to-be-determined prefix from object names in an enclosing scope. + */ + def pushContext(): NamingContext = { + val context = new NamingContext + namingStack.push(context) + context + } + + /** Called at the end of a function, popping the current naming context, adding it to the + * enclosing context's descendants, and passing through the prefix naming reference. + * Every instance of push_context() must have a matching pop_context(). + * + * Will assert out if the context being popped isn't the topmost on the stack. + */ + def popContext[T <: Any](prefixRef: T, until: NamingContext): Unit = { + assert(namingStack.top == until) + namingStack.pop() + if (!namingStack.isEmpty) { + namingStack.top.addDescendant(prefixRef, until) + } + } + + def length() : Int = namingStack.length +} diff --git a/core/src/main/scala/chisel3/internal/SourceInfo.scala b/core/src/main/scala/chisel3/internal/SourceInfo.scala new file mode 100644 index 00000000..f1130db4 --- /dev/null +++ b/core/src/main/scala/chisel3/internal/SourceInfo.scala @@ -0,0 +1,61 @@ +// See LICENSE for license details. + +// This file contains macros for adding source locators at the point of invocation. +// +// This is not part of coreMacros to disallow this macro from being implicitly invoked in Chisel +// frontend (and generating source locators in Chisel core), which is almost certainly a bug. +// +// Note: While these functions and definitions are not private (macros can't be +// private), these are NOT meant to be part of the public API (yet) and no +// forward compatibility guarantees are made. +// A future revision may stabilize the source locator API to allow library +// writers to append source locator information at the point of a library +// function invocation. + +package chisel3.internal.sourceinfo + +import scala.language.experimental.macros +import scala.reflect.macros.blackbox.Context + +/** Abstract base class for generalized source information. + */ +sealed trait SourceInfo { + /** A prettier toString + * + * Make a useful message if SourceInfo is available, nothing otherwise + */ + def makeMessage(f: String => String): String +} + +sealed trait NoSourceInfo extends SourceInfo { + def makeMessage(f: String => String): String = "" +} + +/** For when source info can't be generated because of a technical limitation, like for Reg because + * Scala macros don't support named or default arguments. + */ +case object UnlocatableSourceInfo extends NoSourceInfo + +/** For when source info isn't generated because the function is deprecated and we're lazy. + */ +case object DeprecatedSourceInfo extends NoSourceInfo + +/** For FIRRTL lines from a Scala source line. + */ +case class SourceLine(filename: String, line: Int, col: Int) extends SourceInfo { + def makeMessage(f: String => String): String = f(s"@[$filename $line:$col]") +} + +/** Provides a macro that returns the source information at the invocation point. + */ +object SourceInfoMacro { + def generate_source_info(c: Context): c.Tree = { + import c.universe._ + val p = c.enclosingPosition + q"_root_.chisel3.internal.sourceinfo.SourceLine(${p.source.file.name}, ${p.line}, ${p.column})" + } +} + +object SourceInfo { + implicit def materialize: SourceInfo = macro SourceInfoMacro.generate_source_info +} diff --git a/core/src/main/scala/chisel3/internal/firrtl/Converter.scala b/core/src/main/scala/chisel3/internal/firrtl/Converter.scala new file mode 100644 index 00000000..5c1d6935 --- /dev/null +++ b/core/src/main/scala/chisel3/internal/firrtl/Converter.scala @@ -0,0 +1,275 @@ +// See LICENSE for license details. + +package chisel3.internal.firrtl +import chisel3._ +import chisel3.experimental._ +import chisel3.internal.sourceinfo.{NoSourceInfo, SourceLine, SourceInfo} +import firrtl.{ir => fir} +import chisel3.internal.{castToInt, throwException} + +import scala.annotation.tailrec +import scala.collection.immutable.Queue + +private[chisel3] object Converter { + // TODO modeled on unpack method on Printable, refactor? + def unpack(pable: Printable, ctx: Component): (String, Seq[Arg]) = pable match { + case Printables(pables) => + val (fmts, args) = pables.map(p => unpack(p, ctx)).unzip + (fmts.mkString, args.flatten.toSeq) + case PString(str) => (str.replaceAll("%", "%%"), List.empty) + case format: FirrtlFormat => + ("%" + format.specifier, List(format.bits.ref)) + case Name(data) => (data.ref.name, List.empty) + case FullName(data) => (data.ref.fullName(ctx), List.empty) + case Percent => ("%%", List.empty) + } + + def convert(info: SourceInfo): fir.Info = info match { + case _: NoSourceInfo => fir.NoInfo + case SourceLine(fn, line, col) => fir.FileInfo(fir.StringLit(s"$fn $line:$col")) + } + + def convert(op: PrimOp): fir.PrimOp = firrtl.PrimOps.fromString(op.name) + + def convert(dir: MemPortDirection): firrtl.MPortDir = dir match { + case MemPortDirection.INFER => firrtl.MInfer + case MemPortDirection.READ => firrtl.MRead + case MemPortDirection.WRITE => firrtl.MWrite + case MemPortDirection.RDWR => firrtl.MReadWrite + } + + // TODO + // * Memoize? + // * Move into the Chisel IR? + def convert(arg: Arg, ctx: Component): fir.Expression = arg match { // scalastyle:ignore cyclomatic.complexity + case Node(id) => + convert(id.getRef, ctx) + case Ref(name) => + fir.Reference(name, fir.UnknownType) + case Slot(imm, name) => + fir.SubField(convert(imm, ctx), name, fir.UnknownType) + case Index(imm, ILit(idx)) => + fir.SubIndex(convert(imm, ctx), castToInt(idx, "Index"), fir.UnknownType) + case Index(imm, value) => + fir.SubAccess(convert(imm, ctx), convert(value, ctx), fir.UnknownType) + case ModuleIO(mod, name) => + // scalastyle:off if.brace + if (mod eq ctx.id) fir.Reference(name, fir.UnknownType) + else fir.SubField(fir.Reference(mod.getRef.name, fir.UnknownType), name, fir.UnknownType) + // scalastyle:on if.brace + case u @ ULit(n, UnknownWidth()) => + fir.UIntLiteral(n, fir.IntWidth(u.minWidth)) + case ULit(n, w) => + fir.UIntLiteral(n, convert(w)) + case slit @ SLit(n, w) => fir.SIntLiteral(n, convert(w)) + val unsigned = if (n < 0) (BigInt(1) << slit.width.get) + n else n + val uint = convert(ULit(unsigned, slit.width), ctx) + fir.DoPrim(firrtl.PrimOps.AsSInt, Seq(uint), Seq.empty, fir.UnknownType) + // TODO Simplify + case fplit @ FPLit(n, w, bp) => + val unsigned = if (n < 0) (BigInt(1) << fplit.width.get) + n else n + val uint = convert(ULit(unsigned, fplit.width), ctx) + val lit = bp.asInstanceOf[KnownBinaryPoint].value + fir.DoPrim(firrtl.PrimOps.AsFixedPoint, Seq(uint), Seq(lit), fir.UnknownType) + case intervalLit @ IntervalLit(n, w, bp) => + val unsigned = if (n < 0) (BigInt(1) << intervalLit.width.get) + n else n + val uint = convert(ULit(unsigned, intervalLit.width), ctx) + val lit = bp.asInstanceOf[KnownBinaryPoint].value + fir.DoPrim(firrtl.PrimOps.AsInterval, Seq(uint), Seq(n, n, lit), fir.UnknownType) + case lit: ILit => + throwException(s"Internal Error! Unexpected ILit: $lit") + } + + /** Convert Commands that map 1:1 to Statements */ + def convertSimpleCommand(cmd: Command, ctx: Component): Option[fir.Statement] = cmd match { // scalastyle:ignore cyclomatic.complexity line.size.limit + case e: DefPrim[_] => + val consts = e.args.collect { case ILit(i) => i } + val args = e.args.flatMap { + case _: ILit => None + case other => Some(convert(other, ctx)) + } + val expr = e.op.name match { + case "mux" => + assert(args.size == 3, s"Mux with unexpected args: $args") + fir.Mux(args(0), args(1), args(2), fir.UnknownType) + case _ => + fir.DoPrim(convert(e.op), args, consts, fir.UnknownType) + } + Some(fir.DefNode(convert(e.sourceInfo), e.name, expr)) + case e @ DefWire(info, id) => + Some(fir.DefWire(convert(info), e.name, extractType(id))) + case e @ DefReg(info, id, clock) => + Some(fir.DefRegister(convert(info), e.name, extractType(id), convert(clock, ctx), + firrtl.Utils.zero, convert(id.getRef, ctx))) + case e @ DefRegInit(info, id, clock, reset, init) => + Some(fir.DefRegister(convert(info), e.name, extractType(id), convert(clock, ctx), + convert(reset, ctx), convert(init, ctx))) + case e @ DefMemory(info, id, t, size) => + Some(firrtl.CDefMemory(convert(info), e.name, extractType(t), size, false)) + case e @ DefSeqMemory(info, id, t, size, ruw) => + Some(firrtl.CDefMemory(convert(info), e.name, extractType(t), size, true, ruw)) + case e: DefMemPort[_] => + Some(firrtl.CDefMPort(convert(e.sourceInfo), e.name, fir.UnknownType, + e.source.fullName(ctx), Seq(convert(e.index, ctx), convert(e.clock, ctx)), convert(e.dir))) + case Connect(info, loc, exp) => + Some(fir.Connect(convert(info), convert(loc, ctx), convert(exp, ctx))) + case BulkConnect(info, loc, exp) => + Some(fir.PartialConnect(convert(info), convert(loc, ctx), convert(exp, ctx))) + case Attach(info, locs) => + Some(fir.Attach(convert(info), locs.map(l => convert(l, ctx)))) + case DefInvalid(info, arg) => + Some(fir.IsInvalid(convert(info), convert(arg, ctx))) + case e @ DefInstance(info, id, _) => + Some(fir.DefInstance(convert(info), e.name, id.name)) + case Stop(info, clock, ret) => + Some(fir.Stop(convert(info), ret, convert(clock, ctx), firrtl.Utils.one)) + case Printf(info, clock, pable) => + val (fmt, args) = unpack(pable, ctx) + Some(fir.Print(convert(info), fir.StringLit(fmt), + args.map(a => convert(a, ctx)), convert(clock, ctx), firrtl.Utils.one)) + case _ => None + } + + /** Internal datastructure to help translate Chisel's flat Command structure to FIRRTL's AST + * + * In particular, when scoping is translated from flat with begin end to a nested datastructure + * + * @param when Current when Statement, holds info, condition, and consequence as they are + * available + * @param outer Already converted Statements that precede the current when block in the scope in + * which the when is defined (ie. 1 level up from the scope inside the when) + * @param alt Indicates if currently processing commands in the alternate (else) of the when scope + */ + // TODO we should probably have a different structure in the IR to close elses + private case class WhenFrame(when: fir.Conditionally, outer: Queue[fir.Statement], alt: Boolean) + + /** Convert Chisel IR Commands into FIRRTL Statements + * + * @note ctx is needed because references to ports translate differently when referenced within + * the module in which they are defined vs. parent modules + * @param cmds Chisel IR Commands to convert + * @param ctx Component (Module) context within which we are translating + * @return FIRRTL Statement that is equivalent to the input cmds + */ + def convert(cmds: Seq[Command], ctx: Component): fir.Statement = { // scalastyle:ignore cyclomatic.complexity + @tailrec + // scalastyle:off if.brace + def rec(acc: Queue[fir.Statement], + scope: List[WhenFrame]) + (cmds: Seq[Command]): Seq[fir.Statement] = { + if (cmds.isEmpty) { + assert(scope.isEmpty) + acc + } else convertSimpleCommand(cmds.head, ctx) match { + // Most Commands map 1:1 + case Some(stmt) => + rec(acc :+ stmt, scope)(cmds.tail) + // When scoping logic does not map 1:1 and requires pushing/popping WhenFrames + // Please see WhenFrame for more details + case None => cmds.head match { + case WhenBegin(info, pred) => + val when = fir.Conditionally(convert(info), convert(pred, ctx), fir.EmptyStmt, fir.EmptyStmt) + val frame = WhenFrame(when, acc, false) + rec(Queue.empty, frame +: scope)(cmds.tail) + case WhenEnd(info, depth, _) => + val frame = scope.head + val when = if (frame.alt) frame.when.copy(alt = fir.Block(acc)) + else frame.when.copy(conseq = fir.Block(acc)) + // Check if this when has an else + cmds.tail.headOption match { + case Some(AltBegin(_)) => + assert(!frame.alt, "Internal Error! Unexpected when structure!") // Only 1 else per when + rec(Queue.empty, frame.copy(when = when, alt = true) +: scope.tail)(cmds.drop(2)) + case _ => // Not followed by otherwise + // If depth > 0 then we need to close multiple When scopes so we add a new WhenEnd + // If we're nested we need to add more WhenEnds to ensure each When scope gets + // properly closed + val cmdsx = if (depth > 0) WhenEnd(info, depth - 1, false) +: cmds.tail else cmds.tail + rec(frame.outer :+ when, scope.tail)(cmdsx) + } + case OtherwiseEnd(info, depth) => + val frame = scope.head + val when = frame.when.copy(alt = fir.Block(acc)) + // TODO For some reason depth == 1 indicates the last closing otherwise whereas + // depth == 0 indicates last closing when + val cmdsx = if (depth > 1) OtherwiseEnd(info, depth - 1) +: cmds.tail else cmds.tail + rec(scope.head.outer :+ when, scope.tail)(cmdsx) + } + } + } + // scalastyle:on if.brace + fir.Block(rec(Queue.empty, List.empty)(cmds)) + } + + def convert(width: Width): fir.Width = width match { + case UnknownWidth() => fir.UnknownWidth + case KnownWidth(value) => fir.IntWidth(value) + } + + def convert(bp: BinaryPoint): fir.Width = bp match { + case UnknownBinaryPoint => fir.UnknownWidth + case KnownBinaryPoint(value) => fir.IntWidth(value) + } + + private def firrtlUserDirOf(d: Data): SpecifiedDirection = d match { + case d: Vec[_] => + SpecifiedDirection.fromParent(d.specifiedDirection, firrtlUserDirOf(d.sample_element)) + case d => d.specifiedDirection + } + + def extractType(data: Data, clearDir: Boolean = false): fir.Type = data match { // scalastyle:ignore cyclomatic.complexity line.size.limit + case _: Clock => fir.ClockType + case _: AsyncReset => fir.AsyncResetType + case _: ResetType => fir.ResetType + case d: EnumType => fir.UIntType(convert(d.width)) + case d: UInt => fir.UIntType(convert(d.width)) + case d: SInt => fir.SIntType(convert(d.width)) + case d: FixedPoint => fir.FixedType(convert(d.width), convert(d.binaryPoint)) + case d: Interval => fir.IntervalType(d.range.lowerBound, d.range.upperBound, d.range.firrtlBinaryPoint) + case d: Analog => fir.AnalogType(convert(d.width)) + case d: Vec[_] => fir.VectorType(extractType(d.sample_element, clearDir), d.length) + case d: Record => + val childClearDir = clearDir || + d.specifiedDirection == SpecifiedDirection.Input || d.specifiedDirection == SpecifiedDirection.Output + def eltField(elt: Data): fir.Field = (childClearDir, firrtlUserDirOf(elt)) match { + case (true, _) => fir.Field(elt.getRef.name, fir.Default, extractType(elt, true)) + case (false, SpecifiedDirection.Unspecified | SpecifiedDirection.Output) => + fir.Field(elt.getRef.name, fir.Default, extractType(elt, false)) + case (false, SpecifiedDirection.Flip | SpecifiedDirection.Input) => + fir.Field(elt.getRef.name, fir.Flip, extractType(elt, false)) + } + fir.BundleType(d.elements.toIndexedSeq.reverse.map { case (_, e) => eltField(e) }) + } + + def convert(name: String, param: Param): fir.Param = param match { + case IntParam(value) => fir.IntParam(name, value) + case DoubleParam(value) => fir.DoubleParam(name, value) + case StringParam(value) => fir.StringParam(name, fir.StringLit(value)) + case RawParam(value) => fir.RawStringParam(name, value) + } + def convert(port: Port, topDir: SpecifiedDirection = SpecifiedDirection.Unspecified): fir.Port = { + val resolvedDir = SpecifiedDirection.fromParent(topDir, port.dir) + val dir = resolvedDir match { + case SpecifiedDirection.Unspecified | SpecifiedDirection.Output => fir.Output + case SpecifiedDirection.Flip | SpecifiedDirection.Input => fir.Input + } + val clearDir = resolvedDir match { + case SpecifiedDirection.Input | SpecifiedDirection.Output => true + case SpecifiedDirection.Unspecified | SpecifiedDirection.Flip => false + } + val tpe = extractType(port.id, clearDir) + fir.Port(fir.NoInfo, port.id.getRef.name, dir, tpe) + } + + def convert(component: Component): fir.DefModule = component match { + case ctx @ DefModule(_, name, ports, cmds) => + fir.Module(fir.NoInfo, name, ports.map(p => convert(p)), convert(cmds.toList, ctx)) + case ctx @ DefBlackBox(id, name, ports, topDir, params) => + fir.ExtModule(fir.NoInfo, name, ports.map(p => convert(p, topDir)), id.desiredName, + params.map { case (name, p) => convert(name, p) }.toSeq) + } + + def convert(circuit: Circuit): fir.Circuit = + fir.Circuit(fir.NoInfo, circuit.components.map(convert), circuit.name) +} + diff --git a/core/src/main/scala/chisel3/internal/firrtl/IR.scala b/core/src/main/scala/chisel3/internal/firrtl/IR.scala new file mode 100644 index 00000000..d98bebcd --- /dev/null +++ b/core/src/main/scala/chisel3/internal/firrtl/IR.scala @@ -0,0 +1,750 @@ +// See LICENSE for license details. + +package chisel3.internal.firrtl + +import firrtl.{ir => fir} + +import chisel3._ +import chisel3.internal._ +import chisel3.internal.sourceinfo.SourceInfo +import chisel3.experimental._ +import _root_.firrtl.{ir => firrtlir} +import _root_.firrtl.PrimOps + +import scala.collection.immutable.NumericRange +import scala.math.BigDecimal.RoundingMode + +// scalastyle:off number.of.types + +case class PrimOp(name: String) { + override def toString: String = name +} + +object PrimOp { + val AddOp = PrimOp("add") + val SubOp = PrimOp("sub") + val TailOp = PrimOp("tail") + val HeadOp = PrimOp("head") + val TimesOp = PrimOp("mul") + val DivideOp = PrimOp("div") + val RemOp = PrimOp("rem") + val ShiftLeftOp = PrimOp("shl") + val ShiftRightOp = PrimOp("shr") + val DynamicShiftLeftOp = PrimOp("dshl") + val DynamicShiftRightOp = PrimOp("dshr") + val BitAndOp = PrimOp("and") + val BitOrOp = PrimOp("or") + val BitXorOp = PrimOp("xor") + val BitNotOp = PrimOp("not") + val ConcatOp = PrimOp("cat") + val BitsExtractOp = PrimOp("bits") + val LessOp = PrimOp("lt") + val LessEqOp = PrimOp("leq") + val GreaterOp = PrimOp("gt") + val GreaterEqOp = PrimOp("geq") + val EqualOp = PrimOp("eq") + val PadOp = PrimOp("pad") + val NotEqualOp = PrimOp("neq") + val NegOp = PrimOp("neg") + val MultiplexOp = PrimOp("mux") + val AndReduceOp = PrimOp("andr") + val OrReduceOp = PrimOp("orr") + val XorReduceOp = PrimOp("xorr") + val ConvertOp = PrimOp("cvt") + val AsUIntOp = PrimOp("asUInt") + val AsSIntOp = PrimOp("asSInt") + val AsFixedPointOp = PrimOp("asFixedPoint") + val AsIntervalOp = PrimOp("asInterval") + val WrapOp = PrimOp("wrap") + val SqueezeOp = PrimOp("squz") + val ClipOp = PrimOp("clip") + val SetBinaryPoint = PrimOp("setp") + val IncreasePrecision = PrimOp("incp") + val DecreasePrecision = PrimOp("decp") + val AsClockOp = PrimOp("asClock") + val AsAsyncResetOp = PrimOp("asAsyncReset") +} + +abstract class Arg { + def fullName(ctx: Component): String = name + def name: String +} + +case class Node(id: HasId) extends Arg { + override def fullName(ctx: Component): String = id.getOptionRef match { + case Some(arg) => arg.fullName(ctx) + case None => id.suggestedName.getOrElse("??") + } + def name: String = id.getOptionRef match { + case Some(arg) => arg.name + case None => id.suggestedName.getOrElse("??") + } +} + +abstract class LitArg(val num: BigInt, widthArg: Width) extends Arg { + private[chisel3] def forcedWidth = widthArg.known + private[chisel3] def width: Width = if (forcedWidth) widthArg else Width(minWidth) + override def fullName(ctx: Component): String = name + // Ensure the node representing this LitArg has a ref to it and a literal binding. + def bindLitArg[T <: Element](elem: T): T = { + elem.bind(ElementLitBinding(this)) + elem.setRef(this) + elem + } + + protected def minWidth: Int + if (forcedWidth) { + require(widthArg.get >= minWidth, + s"The literal value ${num} was elaborated with a specified width of ${widthArg.get} bits, but at least ${minWidth} bits are required.") // scalastyle:ignore line.size.limit + } +} + +case class ILit(n: BigInt) extends Arg { + def name: String = n.toString +} + +case class ULit(n: BigInt, w: Width) extends LitArg(n, w) { + def name: String = "UInt" + width + "(\"h0" + num.toString(16) + "\")" + def minWidth: Int = 1 max n.bitLength + + require(n >= 0, s"UInt literal ${n} is negative") +} + +case class SLit(n: BigInt, w: Width) extends LitArg(n, w) { + def name: String = { + val unsigned = if (n < 0) (BigInt(1) << width.get) + n else n + s"asSInt(${ULit(unsigned, width).name})" + } + def minWidth: Int = 1 + n.bitLength +} + +case class FPLit(n: BigInt, w: Width, binaryPoint: BinaryPoint) extends LitArg(n, w) { + def name: String = { + val unsigned = if (n < 0) (BigInt(1) << width.get) + n else n + s"asFixedPoint(${ULit(unsigned, width).name}, ${binaryPoint.asInstanceOf[KnownBinaryPoint].value})" + } + def minWidth: Int = 1 + n.bitLength +} + +case class IntervalLit(n: BigInt, w: Width, binaryPoint: BinaryPoint) extends LitArg(n, w) { + def name: String = { + val unsigned = if (n < 0) (BigInt(1) << width.get) + n else n + s"asInterval(${ULit(unsigned, width).name}, ${n}, ${n}, ${binaryPoint.asInstanceOf[KnownBinaryPoint].value})" + } + val range: IntervalRange = { + new IntervalRange(IntervalRange.getBound(isClosed = true, BigDecimal(n)), + IntervalRange.getBound(isClosed = true, BigDecimal(n)), IntervalRange.getRangeWidth(binaryPoint)) + } + def minWidth: Int = 1 + n.bitLength +} + +case class Ref(name: String) extends Arg +case class ModuleIO(mod: BaseModule, name: String) extends Arg { + override def fullName(ctx: Component): String = + if (mod eq ctx.id) name else s"${mod.getRef.name}.$name" +} +case class Slot(imm: Node, name: String) extends Arg { + override def fullName(ctx: Component): String = + if (imm.fullName(ctx).isEmpty) name else s"${imm.fullName(ctx)}.${name}" +} +case class Index(imm: Arg, value: Arg) extends Arg { + def name: String = s"[$value]" + override def fullName(ctx: Component): String = s"${imm.fullName(ctx)}[${value.fullName(ctx)}]" +} + +object Width { + def apply(x: Int): Width = KnownWidth(x) + def apply(): Width = UnknownWidth() +} + +sealed abstract class Width { + type W = Int + def max(that: Width): Width = this.op(that, _ max _) + def + (that: Width): Width = this.op(that, _ + _) + def + (that: Int): Width = this.op(this, (a, b) => a + that) + def shiftRight(that: Int): Width = this.op(this, (a, b) => 0 max (a - that)) + def dynamicShiftLeft(that: Width): Width = + this.op(that, (a, b) => a + (1 << b) - 1) + + def known: Boolean + def get: W + protected def op(that: Width, f: (W, W) => W): Width +} + +sealed case class UnknownWidth() extends Width { + def known: Boolean = false + def get: Int = None.get + def op(that: Width, f: (W, W) => W): Width = this + override def toString: String = "" +} + +sealed case class KnownWidth(value: Int) extends Width { + require(value >= 0) + def known: Boolean = true + def get: Int = value + def op(that: Width, f: (W, W) => W): Width = that match { + case KnownWidth(x) => KnownWidth(f(value, x)) + case _ => that + } + override def toString: String = s"<${value.toString}>" +} + +object BinaryPoint { + def apply(x: Int): BinaryPoint = KnownBinaryPoint(x) + def apply(): BinaryPoint = UnknownBinaryPoint +} + +sealed abstract class BinaryPoint { + type W = Int + def max(that: BinaryPoint): BinaryPoint = this.op(that, _ max _) + def + (that: BinaryPoint): BinaryPoint = this.op(that, _ + _) + def + (that: Int): BinaryPoint = this.op(this, (a, b) => a + that) + def shiftRight(that: Int): BinaryPoint = this.op(this, (a, b) => 0 max (a - that)) + def dynamicShiftLeft(that: BinaryPoint): BinaryPoint = + this.op(that, (a, b) => a + (1 << b) - 1) + + def known: Boolean + def get: W + protected def op(that: BinaryPoint, f: (W, W) => W): BinaryPoint +} + +case object UnknownBinaryPoint extends BinaryPoint { + def known: Boolean = false + def get: Int = None.get + def op(that: BinaryPoint, f: (W, W) => W): BinaryPoint = this + override def toString: String = "" +} + +sealed case class KnownBinaryPoint(value: Int) extends BinaryPoint { + def known: Boolean = true + def get: Int = value + def op(that: BinaryPoint, f: (W, W) => W): BinaryPoint = that match { + case KnownBinaryPoint(x) => KnownBinaryPoint(f(value, x)) + case _ => that + } + override def toString: String = s"<<${value.toString}>>" +} + + +sealed abstract class MemPortDirection(name: String) { + override def toString: String = name +} +object MemPortDirection { + object READ extends MemPortDirection("read") + object WRITE extends MemPortDirection("write") + object RDWR extends MemPortDirection("rdwr") + object INFER extends MemPortDirection("infer") +} + +sealed trait RangeType { + def getWidth: Width + + def * (that: IntervalRange): IntervalRange + def +& (that: IntervalRange): IntervalRange + def -& (that: IntervalRange): IntervalRange + def << (that: Int): IntervalRange + def >> (that: Int): IntervalRange + def << (that: KnownWidth): IntervalRange + def >> (that: KnownWidth): IntervalRange + def merge(that: IntervalRange): IntervalRange +} + +object IntervalRange { + /** Creates an IntervalRange, this is used primarily by the range interpolator macro + * @param lower lower bound + * @param upper upper bound + * @param firrtlBinaryPoint binary point firrtl style + * @return + */ + def apply(lower: firrtlir.Bound, upper: firrtlir.Bound, firrtlBinaryPoint: firrtlir.Width): IntervalRange = { + new IntervalRange(lower, upper, firrtlBinaryPoint) + } + + def apply(lower: firrtlir.Bound, upper: firrtlir.Bound, binaryPoint: BinaryPoint): IntervalRange = { + new IntervalRange(lower, upper, IntervalRange.getBinaryPoint(binaryPoint)) + } + + def apply(lower: firrtlir.Bound, upper: firrtlir.Bound, binaryPoint: Int): IntervalRange = { + IntervalRange(lower, upper, BinaryPoint(binaryPoint)) + } + + /** Returns an IntervalRange appropriate for a signed value of the given width + * @param binaryPoint number of bits of mantissa + * @return + */ + def apply(binaryPoint: BinaryPoint): IntervalRange = { + IntervalRange(firrtlir.UnknownBound, firrtlir.UnknownBound, binaryPoint) + } + + /** Returns an IntervalRange appropriate for a signed value of the given width + * @param width number of bits to have in the interval + * @param binaryPoint number of bits of mantissa + * @return + */ + def apply(width: Width, binaryPoint: BinaryPoint = 0.BP): IntervalRange = { + val range = width match { + case KnownWidth(w) => + val nearestPowerOf2 = BigInt("1" + ("0" * (w - 1)), 2) + IntervalRange( + firrtlir.Closed(BigDecimal(-nearestPowerOf2)), firrtlir.Closed(BigDecimal(nearestPowerOf2 - 1)), binaryPoint + ) + case _ => + IntervalRange(firrtlir.UnknownBound, firrtlir.UnknownBound, binaryPoint) + } + range + } + + def unapply(arg: IntervalRange): Option[(firrtlir.Bound, firrtlir.Bound, BinaryPoint)] = { + return Some((arg.lower, arg.upper, arg.binaryPoint)) + } + + def getBound(isClosed: Boolean, value: String): firrtlir.Bound = { + if(value == "?") { + firrtlir.UnknownBound + } + else if(isClosed) { + firrtlir.Closed(BigDecimal(value)) + } + else { + firrtlir.Open(BigDecimal(value)) + } + } + + def getBound(isClosed: Boolean, value: BigDecimal): firrtlir.Bound = { + if(isClosed) { + firrtlir.Closed(value) + } + else { + firrtlir.Open(value) + } + } + + def getBound(isClosed: Boolean, value: Int): firrtlir.Bound = { + getBound(isClosed, (BigDecimal(value))) + } + + def getBinaryPoint(s: String): firrtlir.Width = { + firrtlir.UnknownWidth + } + + def getBinaryPoint(n: Int): firrtlir.Width = { + if(n < 0) { + firrtlir.UnknownWidth + } + else { + firrtlir.IntWidth(n) + } + } + def getBinaryPoint(n: BinaryPoint): firrtlir.Width = { + n match { + case UnknownBinaryPoint => firrtlir.UnknownWidth + case KnownBinaryPoint(w) => firrtlir.IntWidth(w) + } + } + + def getRangeWidth(w: Width): firrtlir.Width = { + if(w.known) { + firrtlir.IntWidth(w.get) + } + else { + firrtlir.UnknownWidth + } + } + def getRangeWidth(binaryPoint: BinaryPoint): firrtlir.Width = { + if(binaryPoint.known) { + firrtlir.IntWidth(binaryPoint.get) + } + else { + firrtlir.UnknownWidth + } + } + + //scalastyle:off method.name + def Unknown: IntervalRange = range"[?,?].?" +} + + +sealed class IntervalRange( + val lowerBound: firrtlir.Bound, + val upperBound: firrtlir.Bound, + private[chisel3] val firrtlBinaryPoint: firrtlir.Width) + extends firrtlir.IntervalType(lowerBound, upperBound, firrtlBinaryPoint) + with RangeType { + + (lowerBound, upperBound) match { + case (firrtlir.Open(begin), firrtlir.Open(end)) => + if(begin >= end) throw new ChiselException(s"Invalid range with ${serialize}") + binaryPoint match { + case KnownBinaryPoint(bp) => + if(begin >= end - (BigDecimal(1) / BigDecimal(BigInt(1) << bp))) { + throw new ChiselException(s"Invalid range with ${serialize}") + } + case _ => + } + case (firrtlir.Open(begin), firrtlir.Closed(end)) => + if(begin >= end) throw new ChiselException(s"Invalid range with ${serialize}") + case (firrtlir.Closed(begin), firrtlir.Open(end)) => + if(begin >= end) throw new ChiselException(s"Invalid range with ${serialize}") + case (firrtlir.Closed(begin), firrtlir.Closed(end)) => + if(begin > end) throw new ChiselException(s"Invalid range with ${serialize}") + case _ => + } + + //scalastyle:off cyclomatic.complexity + override def toString: String = { + val binaryPoint = firrtlBinaryPoint match { + case firrtlir.IntWidth(n) => s"$n" + case _ => "?" + } + val lowerBoundString = lowerBound match { + case firrtlir.Closed(l) => s"[$l" + case firrtlir.Open(l) => s"($l" + case firrtlir.UnknownBound => s"[?" + } + val upperBoundString = upperBound match { + case firrtlir.Closed(l) => s"$l]" + case firrtlir.Open(l) => s"$l)" + case firrtlir.UnknownBound => s"?]" + } + s"""range"$lowerBoundString,$upperBoundString.$binaryPoint"""" + } + + val increment: Option[BigDecimal] = firrtlBinaryPoint match { + case firrtlir.IntWidth(bp) => + Some(BigDecimal(math.pow(2, -bp.doubleValue))) + case _ => None + } + + /** If possible returns the lowest possible value for this Interval + * @return + */ + val getLowestPossibleValue: Option[BigDecimal] = { + increment match { + case Some(inc) => + lower match { + case firrtlir.Closed(n) => Some(n) + case firrtlir.Open(n) => Some(n + inc) + case _ => None + } + case _ => + None + } + } + + /** If possible returns the highest possible value for this Interval + * @return + */ + val getHighestPossibleValue: Option[BigDecimal] = { + increment match { + case Some(inc) => + upper match { + case firrtlir.Closed(n) => Some(n) + case firrtlir.Open(n) => Some(n - inc) + case _ => None + } + case _ => + None + } + } + + /** Return a Seq of the possible values for this range + * Mostly to be used for testing + * @return + */ + def getPossibleValues: NumericRange[BigDecimal] = { + (getLowestPossibleValue, getHighestPossibleValue, increment) match { + case (Some(low), Some(high), Some(inc)) => (low to high by inc) + case (_, _, None) => + throw new ChiselException(s"BinaryPoint unknown. Cannot get possible values from IntervalRange $toString") + case _ => + throw new ChiselException(s"Unknown Bound. Cannot get possible values from IntervalRange $toString") + + } + } + + override def getWidth: Width = { + width match { + case firrtlir.IntWidth(n) => KnownWidth(n.toInt) + case firrtlir.UnknownWidth => UnknownWidth() + } + } + + private def doFirrtlOp(op: firrtlir.PrimOp, that: IntervalRange): IntervalRange = { + PrimOps.set_primop_type( + firrtlir.DoPrim(op, + Seq(firrtlir.Reference("a", this), firrtlir.Reference("b", that)), Nil,firrtlir.UnknownType) + ).tpe match { + case i: firrtlir.IntervalType => IntervalRange(i.lower, i.upper, i.point) + case other => sys.error("BAD!") + } + } + + private def doFirrtlDynamicShift(that: UInt, isLeft: Boolean): IntervalRange = { + val uinttpe = that.widthOption match { + case None => firrtlir.UIntType(firrtlir.UnknownWidth) + case Some(w) => firrtlir.UIntType(firrtlir.IntWidth(w)) + } + val op = if(isLeft) PrimOps.Dshl else PrimOps.Dshr + PrimOps.set_primop_type( + firrtlir.DoPrim(op, + Seq(firrtlir.Reference("a", this), firrtlir.Reference("b", uinttpe)), Nil,firrtlir.UnknownType) + ).tpe match { + case i: firrtlir.IntervalType => IntervalRange(i.lower, i.upper, i.point) + case other => sys.error("BAD!") + } + } + + private def doFirrtlOp(op: firrtlir.PrimOp, that: Int): IntervalRange = { + PrimOps.set_primop_type( + firrtlir.DoPrim(op, + Seq(firrtlir.Reference("a", this)), Seq(BigInt(that)), firrtlir.UnknownType) + ).tpe match { + case i: firrtlir.IntervalType => IntervalRange(i.lower, i.upper, i.point) + case other => sys.error("BAD!") + } + } + + /** Multiply this by that, here we return a fully unknown range, + * firrtl's range inference can figure this out + * @param that + * @return + */ + override def *(that: IntervalRange): IntervalRange = { + doFirrtlOp(PrimOps.Mul, that) + } + + /** Add that to this, here we return a fully unknown range, + * firrtl's range inference can figure this out + * @param that + * @return + */ + override def +&(that: IntervalRange): IntervalRange = { + doFirrtlOp(PrimOps.Add, that) + } + + /** Subtract that from this, here we return a fully unknown range, + * firrtl's range inference can figure this out + * @param that + * @return + */ + override def -&(that: IntervalRange): IntervalRange = { + doFirrtlOp(PrimOps.Sub, that) + } + + private def adjustBoundValue(value: BigDecimal, binaryPointValue: Int): BigDecimal = { + if(binaryPointValue >= 0) { + val maskFactor = BigDecimal(1 << binaryPointValue) + val a = (value * maskFactor) + val b = a.setScale(0, RoundingMode.DOWN) + val c = b / maskFactor + c + } else { + value + } + } + + private def adjustBound(bound: firrtlir.Bound, binaryPoint: BinaryPoint): firrtlir.Bound = { + binaryPoint match { + case KnownBinaryPoint(binaryPointValue) => + bound match { + case firrtlir.Open(value) => firrtlir.Open(adjustBoundValue(value, binaryPointValue)) + case firrtlir.Closed(value) => firrtlir.Closed(adjustBoundValue(value, binaryPointValue)) + case _ => bound + } + case _ => firrtlir.UnknownBound + } + } + + /** Creates a new range with the increased precision + * + * @param newBinaryPoint + * @return + */ + def incPrecision(newBinaryPoint: BinaryPoint): IntervalRange = { + newBinaryPoint match { + case KnownBinaryPoint(that) => + doFirrtlOp(PrimOps.IncP, that) + case _ => + throwException(s"$this.incPrecision(newBinaryPoint = $newBinaryPoint) error, newBinaryPoint must be know") + } + } + + /** Creates a new range with the decreased precision + * + * @param newBinaryPoint + * @return + */ + def decPrecision(newBinaryPoint: BinaryPoint): IntervalRange = { + newBinaryPoint match { + case KnownBinaryPoint(that) => + doFirrtlOp(PrimOps.DecP, that) + case _ => + throwException(s"$this.decPrecision(newBinaryPoint = $newBinaryPoint) error, newBinaryPoint must be know") + } + } + + /** Creates a new range with the given binary point, adjusting precision + * on bounds as necessary + * + * @param newBinaryPoint + * @return + */ + def setPrecision(newBinaryPoint: BinaryPoint): IntervalRange = { + newBinaryPoint match { + case KnownBinaryPoint(that) => + doFirrtlOp(PrimOps.SetP, that) + case _ => + throwException(s"$this.setPrecision(newBinaryPoint = $newBinaryPoint) error, newBinaryPoint must be know") + } + } + + /** Shift this range left, i.e. shifts the min and max by the specified amount + * @param that + * @return + */ + override def <<(that: Int): IntervalRange = { + doFirrtlOp(PrimOps.Shl, that) + } + + /** Shift this range left, i.e. shifts the min and max by the known width + * @param that + * @return + */ + override def <<(that: KnownWidth): IntervalRange = { + <<(that.value) + } + + /** Shift this range left, i.e. shifts the min and max by value + * @param that + * @return + */ + def <<(that: UInt): IntervalRange = { + doFirrtlDynamicShift(that, isLeft = true) + } + + /** Shift this range right, i.e. shifts the min and max by the specified amount + * @param that + * @return + */ + override def >>(that: Int): IntervalRange = { + doFirrtlOp(PrimOps.Shr, that) + } + + /** Shift this range right, i.e. shifts the min and max by the known width + * @param that + * @return + */ + override def >>(that: KnownWidth): IntervalRange = { + >>(that.value) + } + + /** Shift this range right, i.e. shifts the min and max by value + * @param that + * @return + */ + def >>(that: UInt): IntervalRange = { + doFirrtlDynamicShift(that, isLeft = false) + } + + /** + * Squeeze returns the intersection of the ranges this interval and that Interval + * @param that + * @return + */ + def squeeze(that: IntervalRange): IntervalRange = { + doFirrtlOp(PrimOps.Squeeze, that) + } + + /** + * Wrap the value of this [[Interval]] into the range of a different Interval with a presumably smaller range. + * @param that + * @return + */ + def wrap(that: IntervalRange): IntervalRange = { + doFirrtlOp(PrimOps.Wrap, that) + } + + /** + * Clip the value of this [[Interval]] into the range of a different Interval with a presumably smaller range. + * @param that + * @return + */ + def clip(that: IntervalRange): IntervalRange = { + doFirrtlOp(PrimOps.Clip, that) + } + + /** merges the ranges of this and that, basically takes lowest low, highest high and biggest bp + * set unknown if any of this or that's value of above is unknown + * Like an union but will slurp up points in between the two ranges that were part of neither + * @param that + * @return + */ + override def merge(that: IntervalRange): IntervalRange = { + val lowest = (this.getLowestPossibleValue, that.getLowestPossibleValue) match { + case (Some(l1), Some(l2)) => + if(l1 < l2) { this.lower } else { that.lower } + case _ => + firrtlir.UnknownBound + } + val highest = (this.getHighestPossibleValue, that.getHighestPossibleValue) match { + case (Some(l1), Some(l2)) => + if(l1 >= l2) { this.lower } else { that.lower } + case _ => + firrtlir.UnknownBound + } + val newBinaryPoint = (this.firrtlBinaryPoint, that.firrtlBinaryPoint) match { + case (firrtlir.IntWidth(b1), firrtlir.IntWidth(b2)) => + if(b1 > b2) { firrtlir.IntWidth(b1)} else { firrtlir.IntWidth(b2) } + case _ => + firrtlir.UnknownWidth + } + IntervalRange(lowest, highest, newBinaryPoint) + } + + def binaryPoint: BinaryPoint = { + firrtlBinaryPoint match { + case firrtlir.IntWidth(n) => + assert(n < Int.MaxValue, s"binary point value $n is out of range") + KnownBinaryPoint(n.toInt) + case _ => UnknownBinaryPoint + } + } +} + +abstract class Command { + def sourceInfo: SourceInfo +} +abstract class Definition extends Command { + def id: HasId + def name: String = id.getRef.name +} +// scalastyle:off line.size.limit +case class DefPrim[T <: Data](sourceInfo: SourceInfo, id: T, op: PrimOp, args: Arg*) extends Definition +case class DefInvalid(sourceInfo: SourceInfo, arg: Arg) extends Command +case class DefWire(sourceInfo: SourceInfo, id: Data) extends Definition +case class DefReg(sourceInfo: SourceInfo, id: Data, clock: Arg) extends Definition +case class DefRegInit(sourceInfo: SourceInfo, id: Data, clock: Arg, reset: Arg, init: Arg) extends Definition +case class DefMemory(sourceInfo: SourceInfo, id: HasId, t: Data, size: BigInt) extends Definition +case class DefSeqMemory(sourceInfo: SourceInfo, id: HasId, t: Data, size: BigInt, readUnderWrite: fir.ReadUnderWrite.Value) extends Definition +case class DefMemPort[T <: Data](sourceInfo: SourceInfo, id: T, source: Node, dir: MemPortDirection, index: Arg, clock: Arg) extends Definition +case class DefInstance(sourceInfo: SourceInfo, id: BaseModule, ports: Seq[Port]) extends Definition +case class WhenBegin(sourceInfo: SourceInfo, pred: Arg) extends Command +case class WhenEnd(sourceInfo: SourceInfo, firrtlDepth: Int, hasAlt: Boolean = false) extends Command +case class AltBegin(sourceInfo: SourceInfo) extends Command +case class OtherwiseEnd(sourceInfo: SourceInfo, firrtlDepth: Int) extends Command +case class Connect(sourceInfo: SourceInfo, loc: Node, exp: Arg) extends Command +case class BulkConnect(sourceInfo: SourceInfo, loc1: Node, loc2: Node) extends Command +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: SpecifiedDirection) +case class Printf(sourceInfo: SourceInfo, clock: Arg, pable: Printable) extends Command +abstract class Component extends Arg { + def id: BaseModule + def name: String + def ports: Seq[Port] +} +case class DefModule(id: RawModule, name: String, ports: Seq[Port], commands: Seq[Command]) extends Component +case class DefBlackBox(id: BaseBlackBox, name: String, ports: Seq[Port], topDir: SpecifiedDirection, params: Map[String, Param]) extends Component + +case class Circuit(name: String, components: Seq[Component], annotations: Seq[ChiselAnnotation] = Seq.empty) diff --git a/core/src/main/scala/chisel3/package.scala b/core/src/main/scala/chisel3/package.scala new file mode 100644 index 00000000..65bfdeb7 --- /dev/null +++ b/core/src/main/scala/chisel3/package.scala @@ -0,0 +1,229 @@ +// See LICENSE for license details. + +import chisel3.internal.firrtl.BinaryPoint + +/** This package contains the main chisel3 API. + */ +package object chisel3 { // scalastyle:ignore package.object.name + import internal.firrtl.{Port, Width} + import internal.Builder + + import scala.language.implicitConversions + + /** + * These implicit classes allow one to convert scala.Int|scala.BigInt to + * Chisel.UInt|Chisel.SInt by calling .asUInt|.asSInt on them, respectively. + * The versions .asUInt(width)|.asSInt(width) are also available to explicitly + * mark a width for the new literal. + * + * Also provides .asBool to scala.Boolean and .asUInt to String + * + * Note that, for stylistic reasons, one should avoid extracting immediately + * after this call using apply, ie. 0.asUInt(1)(0) due to potential for + * confusion (the 1 is a bit length and the 0 is a bit extraction position). + * Prefer storing the result and then extracting from it. + * + * Implementation note: the empty parameter list (like `U()`) is necessary to prevent + * interpreting calls that have a non-Width parameter as a chained apply, otherwise things like + * `0.asUInt(16)` (instead of `16.W`) compile without error and produce undesired results. + */ + implicit class fromBigIntToLiteral(bigint: BigInt) { + /** Int to Bool conversion, allowing compact syntax like 1.B and 0.B + */ + def B: Bool = bigint match { // scalastyle:ignore method.name + case bigint if bigint == 0 => Bool.Lit(false) + case bigint if bigint == 1 => Bool.Lit(true) + case bigint => Builder.error(s"Cannot convert $bigint to Bool, must be 0 or 1"); Bool.Lit(false) + } + /** Int to UInt conversion, recommended style for constants. + */ + def U: UInt = UInt.Lit(bigint, Width()) // scalastyle:ignore method.name + /** Int to SInt conversion, recommended style for constants. + */ + def S: SInt = SInt.Lit(bigint, Width()) // scalastyle:ignore method.name + /** Int to UInt conversion with specified width, recommended style for constants. + */ + def U(width: Width): UInt = UInt.Lit(bigint, width) // scalastyle:ignore method.name + /** Int to SInt conversion with specified width, recommended style for constants. + */ + def S(width: Width): SInt = SInt.Lit(bigint, width) // scalastyle:ignore method.name + + /** Int to UInt conversion, recommended style for variables. + */ + def asUInt(): UInt = UInt.Lit(bigint, Width()) + /** Int to SInt conversion, recommended style for variables. + */ + def asSInt(): SInt = SInt.Lit(bigint, Width()) + /** Int to UInt conversion with specified width, recommended style for variables. + */ + def asUInt(width: Width): UInt = UInt.Lit(bigint, width) + /** Int to SInt conversion with specified width, recommended style for variables. + */ + def asSInt(width: Width): SInt = SInt.Lit(bigint, width) + } + + implicit class fromIntToLiteral(int: Int) extends fromBigIntToLiteral(int) + implicit class fromLongToLiteral(long: Long) extends fromBigIntToLiteral(long) + + implicit class fromStringToLiteral(str: String) { + /** String to UInt parse, recommended style for constants. + */ + def U: UInt = str.asUInt() // scalastyle:ignore method.name + /** String to UInt parse with specified width, recommended style for constants. + */ + def U(width: Width): UInt = str.asUInt(width) // scalastyle:ignore method.name + + /** String to UInt parse, recommended style for variables. + */ + def asUInt(): UInt = { + val bigInt = parse(str) + UInt.Lit(bigInt, Width(bigInt.bitLength max 1)) + } + /** String to UInt parse with specified width, recommended style for variables. + */ + def asUInt(width: Width): UInt = UInt.Lit(parse(str), width) + + protected def parse(n: String): BigInt = { + val (base, num) = n.splitAt(1) + val radix = base match { + case "x" | "h" => 16 + case "d" => 10 + case "o" => 8 + case "b" => 2 + case _ => Builder.error(s"Invalid base $base"); 2 + } + BigInt(num.filterNot(_ == '_'), radix) + } + } + + implicit class fromIntToBinaryPoint(int: Int) { + def BP: BinaryPoint = BinaryPoint(int) // scalastyle:ignore method.name + } + + implicit class fromBooleanToLiteral(boolean: Boolean) { + /** Boolean to Bool conversion, recommended style for constants. + */ + def B: Bool = Bool.Lit(boolean) // scalastyle:ignore method.name + + /** Boolean to Bool conversion, recommended style for variables. + */ + def asBool(): Bool = Bool.Lit(boolean) + } + + // Fixed Point is experimental for now, but we alias the implicit conversion classes here + // to minimize disruption with existing code. + implicit class fromDoubleToLiteral(double: Double) + extends experimental.FixedPoint.Implicits.fromDoubleToLiteral(double) + + implicit class fromBigDecimalToLiteral(bigDecimal: BigDecimal) + extends experimental.FixedPoint.Implicits.fromBigDecimalToLiteral(bigDecimal) + + // Interval is experimental for now, but we alias the implicit conversion classes here + // to minimize disruption with existing code. + implicit class fromIntToLiteralInterval(int: Int) + extends experimental.Interval.Implicits.fromIntToLiteralInterval(int) + + implicit class fromLongToLiteralInterval(long: Long) + extends experimental.Interval.Implicits.fromLongToLiteralInterval(long) + + implicit class fromBigIntToLiteralInterval(bigInt: BigInt) + extends experimental.Interval.Implicits.fromBigIntToLiteralInterval(bigInt) + + implicit class fromDoubleToLiteralInterval(double: Double) + extends experimental.Interval.Implicits.fromDoubleToLiteralInterval(double) + + implicit class fromBigDecimalToLiteralInterval(bigDecimal: BigDecimal) + extends experimental.Interval.Implicits.fromBigDecimalToLiteralInterval(bigDecimal) + + implicit class fromIntToWidth(int: Int) { + def W: Width = Width(int) // scalastyle:ignore method.name + } + + val WireInit = WireDefault + + object Vec extends VecFactory + + // Some possible regex replacements for the literal specifier deprecation: + // (note: these are not guaranteed to handle all edge cases! check all replacements!) + // Bool\((true|false)\) + // => $1.B + // UInt\(width\s*=\s*(\d+|[_a-zA-Z][_0-9a-zA-Z]*)\) + // => UInt($1.W) + // (UInt|SInt|Bits).width\((\d+|[_a-zA-Z][_0-9a-zA-Z]*)\) + // => $1($2.W) + // (U|S)Int\((-?\d+|0[xX][0-9a-fA-F]+)\) + // => $2.$1 + // UInt\((\d+|0[xX][0-9a-fA-F]+),\s*(?:width\s*=)?\s*(\d+|[_a-zA-Z][_0-9a-zA-Z]*)\) + // => $1.U($2.W) + // (UInt|SInt|Bool)\(([_a-zA-Z][_0-9a-zA-Z]*)\) + // => $2.as$1 + // (UInt|SInt)\(([_a-zA-Z][_0-9a-zA-Z]*),\s*(?:width\s*=)?\s*(\d+|[_a-zA-Z][_0-9a-zA-Z]*)\) + // => $2.as$1($3.W) + + object Bits extends UIntFactory + object UInt extends UIntFactory + object SInt extends SIntFactory + object Bool extends BoolFactory + + type InstanceId = internal.InstanceId + + type Module = chisel3.internal.LegacyModule + + /** Implicit for custom Printable string interpolator */ + implicit class PrintableHelper(val sc: StringContext) extends AnyVal { + /** Custom string interpolator for generating Printables: p"..." + * Will call .toString on any non-Printable arguments (mimicking s"...") + */ + def p(args: Any*): Printable = { + sc.checkLengths(args) // Enforce sc.parts.size == pargs.size + 1 + val pargs: Seq[Option[Printable]] = args map { + case p: Printable => Some(p) + case d: Data => Some(d.toPrintable) + case any => for { + v <- Option(any) // Handle null inputs + str = v.toString + if !str.isEmpty // Handle empty Strings + } yield PString(str) + } + val parts = sc.parts map StringContext.treatEscapes + // Zip sc.parts and pargs together ito flat Seq + // eg. Seq(sc.parts(0), pargs(0), sc.parts(1), pargs(1), ...) + val seq = for { // append None because sc.parts.size == pargs.size + 1 + (literal, arg) <- parts zip (pargs :+ None) + optPable <- Seq(Some(PString(literal)), arg) + pable <- optPable // Remove Option[_] + } yield pable + Printables(seq) + } + } + + implicit def string2Printable(str: String): Printable = PString(str) + + type ChiselException = internal.ChiselException + + // Debugger/Tester access to internal Chisel data structures and methods. + def getDataElements(a: Aggregate): Seq[Element] = { + a.allElements + } + def getModulePorts(m: Module): Seq[Port] = m.getPorts + // Invalidate API - a DontCare element for explicit assignment to outputs, + // indicating the signal is intentionally not driven. + val DontCare = chisel3.internal.InternalDontCare + + class BindingException(message: String) extends ChiselException(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) + // Connection exceptions. + case class BiConnectException(message: String) extends ChiselException(message) + case class MonoConnectException(message: String) extends ChiselException(message) +} diff --git a/coreMacros/src/main/scala/chisel3/SourceInfoDoc.scala b/coreMacros/src/main/scala/chisel3/SourceInfoDoc.scala deleted file mode 100644 index c44da915..00000000 --- a/coreMacros/src/main/scala/chisel3/SourceInfoDoc.scala +++ /dev/null @@ -1,38 +0,0 @@ -// See LICENSE for license details. - -package chisel3 - -/** Provides ScalaDoc information for "hidden" `do_*` methods - * - * Mix this into classes/objects that have `do_*` methods to get access to the shared `SourceInfoTransformMacro` - * ScalaDoc group and the lengthy `groupdesc` below. - * - * @groupdesc SourceInfoTransformMacro - * - *

- * '''These internal methods are not part of the public-facing API!''' - *
- *
- * - * The equivalent public-facing methods do not have the `do_` prefix or have the same name. Use and look at the - * documentation for those. If you want left shift, use `<<`, not `do_<<`. If you want conversion to a - * [[scala.collection.Seq Seq]] of [[Bool]]s look at the `asBools` above, not the one below. Users can safely ignore - * every method in this group!

- * - * 🐉🐉🐉 '''Here be dragons...''' 🐉🐉🐉 - *
- *
- * - * These `do_X` methods are used to enable both implicit passing of SourceInfo and [[chisel3.CompileOptions]] - * while also supporting chained apply methods. In effect all "normal" methods that you, as a user, will use in your - * designs, are converted to their "hidden", `do_*`, via macro transformations. Without using macros here, only one - * of the above wanted behaviors is allowed (implicit passing and chained applies)---the compiler interprets a - * chained apply as an explicit 'implicit' argument and will throw type errors.

- * - * The "normal", public-facing methods then take no SourceInfo. However, a macro transforms this public-facing method - * into a call to an internal, hidden `do_*` that takes an explicit SourceInfo by inserting an - * `implicitly[SourceInfo]` as the explicit argument.

- * - * @groupprio SourceInfoTransformMacro 1001 - */ -trait SourceInfoDoc diff --git a/coreMacros/src/main/scala/chisel3/internal/RangeTransform.scala b/coreMacros/src/main/scala/chisel3/internal/RangeTransform.scala deleted file mode 100644 index 0fdbff81..00000000 --- a/coreMacros/src/main/scala/chisel3/internal/RangeTransform.scala +++ /dev/null @@ -1,197 +0,0 @@ -// See LICENSE for license details. - -// Macro transforms that statically (at compile time) parse range specifiers and emit the raw -// (non-human-friendly) range constructor calls. - -package chisel3.internal - -import scala.language.experimental.macros -import scala.reflect.macros.blackbox -import scala.util.matching.Regex - -// Workaround for https://github.com/sbt/sbt/issues/3966 -object RangeTransform { - val UnspecifiedNumber: Regex = """(\?).*""".r - val IntegerNumber: Regex = """(-?\d+).*""".r - val DecimalNumber: Regex = """(-?\d+\.\d+).*""".r -} - -/** Convert the string to IntervalRange, with unknown, open or closed endpoints and a binary point - * ranges looks like - * range"[0,4].1" range starts at 0 inclusive ends at 4.inclusively with a binary point of 1 - * range"(0,4).1" range starts at 0 exclusive ends at 4.exclusively with a binary point of 1 - * - * the min and max of the range are the actually min and max values, thus the binary point - * becomes a sort of multiplier for the number of bits. - * E.g. range"[0,3].2" will require at least 4 bits two provide the two decimal places - * - * @param c contains the string context to be parsed - */ -//scalastyle:off cyclomatic.complexity method.length -class RangeTransform(val c: blackbox.Context) { - import c.universe._ - def apply(args: c.Tree*): c.Tree = { - val stringTrees = c.prefix.tree match { - case q"$_(scala.StringContext.apply(..$strings))" => strings - case _ => - c.abort( - c.enclosingPosition, - s"Range macro unable to parse StringContext, got: ${showCode(c.prefix.tree)}" - ) - } - val strings = stringTrees.map { - case Literal(Constant(string: String)) => string - case tree => - c.abort( - c.enclosingPosition, - s"Range macro unable to parse StringContext element, got: ${showRaw(tree)}" - ) - } - - var nextStringIndex: Int = 1 - var nextArgIndex: Int = 0 - var currString: String = strings.head - - /** Mutably gets the next numeric value in the range specifier. - */ - def computeNextValue(): c.Tree = { - currString = currString.dropWhile(_ == ' ') // allow whitespace - if (currString.isEmpty) { - if (nextArgIndex >= args.length) { - c.abort(c.enclosingPosition, s"Incomplete range specifier") - } - val nextArg = args(nextArgIndex) - nextArgIndex += 1 - - if (nextStringIndex >= strings.length) { - c.abort(c.enclosingPosition, s"Incomplete range specifier") - } - currString = strings(nextStringIndex) - nextStringIndex += 1 - - nextArg - } else { - val nextStringVal = currString match { - case RangeTransform.DecimalNumber(numberString) => numberString - case RangeTransform.IntegerNumber(numberString) => numberString - case RangeTransform.UnspecifiedNumber(_) => "?" - case _ => - c.abort( - c.enclosingPosition, - s"Bad number or unspecified bound $currString" - ) - } - currString = currString.substring(nextStringVal.length) - - if (nextStringVal == "?") { - Literal(Constant("?")) - } else { - c.parse(nextStringVal) - } - } - } - - // Currently, not allowed to have the end stops (inclusive / exclusive) be interpolated. - currString = currString.dropWhile(_ == ' ') - val startInclusive = currString.headOption match { - case Some('[') => true - case Some('(') => false - case Some('?') => - c.abort( - c.enclosingPosition, - s"start of range as unknown s must be '[?' or '(?' not '?'" - ) - case Some(other) => - c.abort( - c.enclosingPosition, - s"Unknown start inclusive/exclusive specifier, got: '$other'" - ) - case None => - c.abort( - c.enclosingPosition, - s"No initial inclusive/exclusive specifier" - ) - } - - currString = currString.substring(1) // eat the inclusive/exclusive specifier - val minArg = computeNextValue() - currString = currString.dropWhile(_ == ' ') - if (currString(0) != ',') { - c.abort(c.enclosingPosition, s"Incomplete range specifier, expected ','") - } - if (currString.head != ',') { - c.abort( - c.enclosingPosition, - s"Incomplete range specifier, expected ',', got $currString" - ) - } - - currString = currString.substring(1) // eat the comma - - val maxArg = computeNextValue() - currString = currString.dropWhile(_ == ' ') - - val endInclusive = currString.headOption match { - case Some(']') => true - case Some(')') => false - case Some('?') => - c.abort( - c.enclosingPosition, - s"start of range as unknown s must be '[?' or '(?' not '?'" - ) - case Some(other) => - c.abort( - c.enclosingPosition, - s"Unknown end inclusive/exclusive specifier, got: '$other' expecting ')' or ']'" - ) - case None => - c.abort( - c.enclosingPosition, - s"Incomplete range specifier, missing end inclusive/exclusive specifier" - ) - } - currString = currString.substring(1) // eat the inclusive/exclusive specifier - currString = currString.dropWhile(_ == ' ') - - val binaryPointString = currString.headOption match { - case Some('.') => - currString = currString.substring(1) - computeNextValue() - case Some(other) => - c.abort( - c.enclosingPosition, - s"Unknown end binary point prefix, got: '$other' was expecting '.'" - ) - case None => - Literal(Constant(0)) - } - - if (nextArgIndex < args.length) { - val unused = args.mkString("") - c.abort( - c.enclosingPosition, - s"Unused interpolated values in range specifier: '$unused'" - ) - } - if (!currString.isEmpty || nextStringIndex < strings.length) { - val unused = currString + strings - .slice(nextStringIndex, strings.length) - .mkString(", ") - c.abort( - c.enclosingPosition, - s"Unused characters in range specifier: '$unused'" - ) - } - - val startBound = - q"_root_.chisel3.internal.firrtl.IntervalRange.getBound($startInclusive, $minArg)" - - val endBound = - q"_root_.chisel3.internal.firrtl.IntervalRange.getBound($endInclusive, $maxArg)" - - val binaryPoint = - q"_root_.chisel3.internal.firrtl.IntervalRange.getBinaryPoint($binaryPointString)" - - q"_root_.chisel3.internal.firrtl.IntervalRange($startBound, $endBound, $binaryPoint)" - } -} diff --git a/coreMacros/src/main/scala/chisel3/internal/RuntimeDeprecationTransform.scala b/coreMacros/src/main/scala/chisel3/internal/RuntimeDeprecationTransform.scala deleted file mode 100644 index e1f528b3..00000000 --- a/coreMacros/src/main/scala/chisel3/internal/RuntimeDeprecationTransform.scala +++ /dev/null @@ -1,42 +0,0 @@ -// See LICENSE for license details. - -package chisel3.internal - -import scala.reflect.macros.whitebox.Context -import scala.annotation.{StaticAnnotation, compileTimeOnly} -import scala.language.experimental.macros - -// Workaround for https://github.com/sbt/sbt/issues/3966 -object RuntimeDeprecatedTransform -class RuntimeDeprecatedTransform(val c: Context) { - import c.universe._ - - /** Adds a Builder.deprecated(...) call based on the contents of a plain @deprecated annotation. - */ - def runtimeDeprecated(annottees: c.Tree*): c.Tree = { - val transformed = annottees.map(annottee => annottee match { - case q"$mods def $tname[..$tparams](...$paramss): $tpt = $expr" => { - val Modifiers(_, _, annotations) = mods - val annotationMessage = annotations.collect { // get all messages from deprecated annotations - case q"new deprecated($desc, $since)" => desc - } match { // ensure there's only one and return it - case msg :: Nil => msg - case _ => c.abort(c.enclosingPosition, s"@chiselRuntimeDeprecated annotion must be used with exactly one @deprecated annotation, got annotations $annotations") // scalastyle:ignore line.size.limit - } - val message = s"$tname is deprecated: $annotationMessage" - val transformedExpr = q""" { - _root_.chisel3.internal.Builder.deprecated($message) - $expr - } """ - q"$mods def $tname[..$tparams](...$paramss): $tpt = $transformedExpr" - } - case other => c.abort(c.enclosingPosition, s"@chiselRuntimeDeprecated annotion may only be used on defs, got ${showCode(other)}") // scalastyle:ignore line.size.limit - }) - q"..$transformed" - } -} - -@compileTimeOnly("enable macro paradise to expand macro annotations") -class chiselRuntimeDeprecated extends StaticAnnotation { - def macroTransform(annottees: Any*): Any = macro chisel3.internal.RuntimeDeprecatedTransform.runtimeDeprecated -} diff --git a/coreMacros/src/main/scala/chisel3/internal/naming/NamingAnnotations.scala b/coreMacros/src/main/scala/chisel3/internal/naming/NamingAnnotations.scala deleted file mode 100644 index bf4879ec..00000000 --- a/coreMacros/src/main/scala/chisel3/internal/naming/NamingAnnotations.scala +++ /dev/null @@ -1,205 +0,0 @@ -// See LICENSE for license details. - -// Transform implementations for name-propagation related annotations. -// -// Helpful references: -// http://docs.scala-lang.org/overviews/quasiquotes/syntax-summary.html#definitions -// for quasiquote structures of various Scala structures -// http://jsuereth.com/scala/2009/02/05/leveraging-annotations-in-scala.html -// use of Transformer -// http://www.scala-lang.org/old/sites/default/files/sids/rytz/Wed,%202010-01-27,%2015:10/annots.pdf -// general annotations reference - -package chisel3.internal.naming - -import scala.reflect.macros.whitebox.Context -import scala.annotation.{StaticAnnotation, compileTimeOnly} -import scala.language.experimental.macros - -// Workaround for https://github.com/sbt/sbt/issues/3966 -object DebugTransforms -class DebugTransforms(val c: Context) { - import c.universe._ - - /** Passthrough transform that prints the annottee for debugging purposes. - * No guarantees are made on what this annotation does, and it may very well change over time. - * - * The print is warning level to make it visually easier to spot, as well as a reminder that - * this annotation should not make it to production / committed code. - */ - def dump(annottees: c.Tree*): c.Tree = { - val combined = annottees.map({ tree => show(tree) }).mkString("\r\n\r\n") - annottees.foreach(tree => c.warning(c.enclosingPosition, s"Debug dump:\n$combined")) - q"..$annottees" - } - - /** Passthrough transform that prints the annottee as a tree for debugging purposes. - * No guarantees are made on what this annotation does, and it may very well change over time. - * - * The print is warning level to make it visually easier to spot, as well as a reminder that - * this annotation should not make it to production / committed code. - */ - def treedump(annottees: c.Tree*): c.Tree = { - val combined = annottees.map({ tree => showRaw(tree) }).mkString("\r\n") - annottees.foreach(tree => c.warning(c.enclosingPosition, s"Debug tree dump:\n$combined")) - q"..$annottees" - } -} - -// Workaround for https://github.com/sbt/sbt/issues/3966 -object NamingTransforms -class NamingTransforms(val c: Context) { - import c.universe._ - - val globalNamingStack = q"_root_.chisel3.internal.DynamicNamingStack" - - /** Base transformer that provides the val name transform. - * Should not be instantiated, since by default this will recurse everywhere and break the - * naming context variable bounds. - */ - trait ValNameTransformer extends Transformer { - val contextVar: TermName - - override def transform(tree: Tree): Tree = tree match { - // Intentionally not prefixed with $mods, since modifiers usually mean the val definition - // is in a non-transformable location, like as a parameter list. - // TODO: is this exhaustive / correct in all cases? - case q"val $tname: $tpt = $expr" => { - val TermName(tnameStr: String) = tname - val transformedExpr = super.transform(expr) - q"val $tname: $tpt = $contextVar.name($transformedExpr, $tnameStr)" - } - case other => super.transform(other) - } - } - - /** Module-specific val name transform, containing logic to prevent from recursing into inner - * classes and applies the naming transform on inner functions. - */ - class ClassBodyTransformer(val contextVar: TermName) extends ValNameTransformer { - override def transform(tree: Tree): Tree = tree match { - case q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }" => // scalastyle:ignore line.size.limit - tree // don't recurse into inner classes - case q"$mods trait $tpname[..$tparams] extends { ..$earlydefns } with ..$parents { $self => ..$stats }" => - tree // don't recurse into inner classes - case q"$mods def $tname[..$tparams](...$paramss): $tpt = $expr" => { - val Modifiers(_, _, annotations) = mods - // don't apply naming transform twice - val containsChiselName = annotations.map({q"new chiselName()" equalsStructure _}).fold(false)({_||_}) - // transforming overloaded initializers causes errors, and the transform isn't helpful - val isInitializer = tname == TermName("") - if (containsChiselName || isInitializer) { - tree - } else { - // apply chiselName transform by default - val transformedExpr = transformHierarchicalMethod(expr) - q"$mods def $tname[..$tparams](...$paramss): $tpt = $transformedExpr" - } - } - case other => super.transform(other) - } - } - - /** Method-specific val name transform, handling the return case. - */ - class MethodTransformer(val contextVar: TermName) extends ValNameTransformer { - override def transform(tree: Tree): Tree = tree match { - // TODO: better error messages when returning nothing - case q"return $expr" => q"return $globalNamingStack.popReturnContext($expr, $contextVar)" - // Do not recurse into methods - case q"$mods def $tname[..$tparams](...$paramss): $tpt = $expr" => tree - case other => super.transform(other) - } - } - - /** Applies the val name transform to a class body. - * Closes context on top level or return local context to englobing context. - * Closing context only makes sense when top level a Module. - * A Module is always the naming top level. - * Transformed classes can be either Module or standard class. - */ - def transformClassBody(stats: List[c.Tree]): Tree = { - val contextVar = TermName(c.freshName("namingContext")) - val transformedBody = (new ClassBodyTransformer(contextVar)).transformTrees(stats) - // Note: passing "this" to popReturnContext is mandatory for propagation through non-module classes - q""" - val $contextVar = $globalNamingStack.pushContext() - ..$transformedBody - if($globalNamingStack.length == 1){ - $contextVar.namePrefix("") - } - $globalNamingStack.popReturnContext(this, $contextVar) - """ - } - - /** Applies the val name transform to a method body, doing additional bookkeeping with the - * context to allow names to propagate and prefix through the function call stack. - */ - def transformHierarchicalMethod(expr: c.Tree): Tree = { - val contextVar = TermName(c.freshName("namingContext")) - val transformedBody = (new MethodTransformer(contextVar)).transform(expr) - - q"""{ - val $contextVar = $globalNamingStack.pushContext() - $globalNamingStack.popReturnContext($transformedBody, $contextVar) - } - """ - } - - /** Applies naming transforms to vals in the annotated module or method. - * - * For methods, a hierarchical naming transform is used, where it will try to give objects names - * based on the call stack, assuming all functions on the stack are annotated as such and return - * a non-AnyVal object. Does not recurse into inner functions. - * - * For modules, this serves as the root of the call stack hierarchy for naming purposes. Methods - * will have chiselName annotations (non-recursively), but this does NOT affect inner classes. - * - * Basically rewrites all instances of: - * val name = expr - * to: - * val name = context.name(expr, name) - */ - def chiselName(annottees: c.Tree*): c.Tree = { - var namedElts: Int = 0 - - val transformed = annottees.map(annottee => annottee match { - // scalastyle:off line.size.limit - case q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }" => { - val transformedStats = transformClassBody(stats) - namedElts += 1 - q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$transformedStats }" - } - case q"$mods object $tname extends { ..$earlydefns } with ..$parents { $self => ..$body }" => { - annottee // Don't fail noisly when a companion object is passed in with the actual class def - } - // Currently disallow on traits, this won't work well with inheritance. - case q"$mods def $tname[..$tparams](...$paramss): $tpt = $expr" => { - val transformedExpr = transformHierarchicalMethod(expr) - namedElts += 1 - q"$mods def $tname[..$tparams](...$paramss): $tpt = $transformedExpr" - } - case other => c.abort(c.enclosingPosition, s"@chiselName annotion may only be used on classes and methods, got ${showCode(other)}") - }) - - if (namedElts != 1) { - c.abort(c.enclosingPosition, s"@chiselName annotation did not match exactly one valid tree, got:\r\n${annottees.map(tree => showCode(tree)).mkString("\r\n\r\n")}") - } - // scalastyle:on line.size.limit - - q"..$transformed" - } -} - -@compileTimeOnly("enable macro paradise to expand macro annotations") -class dump extends StaticAnnotation { // scalastyle:ignore class.name - def macroTransform(annottees: Any*): Any = macro chisel3.internal.naming.DebugTransforms.dump -} -@compileTimeOnly("enable macro paradise to expand macro annotations") -class treedump extends StaticAnnotation { // scalastyle:ignore class.name - def macroTransform(annottees: Any*): Any = macro chisel3.internal.naming.DebugTransforms.treedump -} -@compileTimeOnly("enable macro paradise to expand macro annotations") -class chiselName extends StaticAnnotation { // scalastyle:ignore class.name - def macroTransform(annottees: Any*): Any = macro chisel3.internal.naming.NamingTransforms.chiselName -} diff --git a/coreMacros/src/main/scala/chisel3/internal/sourceinfo/SourceInfoTransform.scala b/coreMacros/src/main/scala/chisel3/internal/sourceinfo/SourceInfoTransform.scala deleted file mode 100644 index 6d7c3411..00000000 --- a/coreMacros/src/main/scala/chisel3/internal/sourceinfo/SourceInfoTransform.scala +++ /dev/null @@ -1,186 +0,0 @@ -// See LICENSE for license details. - -// This file transform macro definitions to explicitly add implicit source info to Chisel method -// calls. - -package chisel3.internal.sourceinfo - -import scala.language.experimental.macros -import scala.reflect.macros.blackbox.Context -import scala.reflect.macros.whitebox - - -/** Transforms a function call so that it can both provide implicit-style source information and - * have a chained apply call. Without macros, only one is possible, since having a implicit - * argument in the definition will cause the compiler to interpret a chained apply as an - * explicit implicit argument and give type errors. - * - * Instead of an implicit argument, the public-facing function no longer takes a SourceInfo at all. - * The macro transforms the public-facing function into a call to an internal function that takes - * an explicit SourceInfo by inserting an implicitly[SourceInfo] as the explicit argument. - */ -trait SourceInfoTransformMacro { - val c: Context - import c.universe._ - def thisObj: Tree = c.prefix.tree - def implicitSourceInfo = q"implicitly[_root_.chisel3.internal.sourceinfo.SourceInfo]" - def implicitCompileOptions = q"implicitly[_root_.chisel3.CompileOptions]" -} - -// Workaround for https://github.com/sbt/sbt/issues/3966 -object UIntTransform -class UIntTransform(val c: Context) extends SourceInfoTransformMacro { - import c.universe._ - def bitset(off: c.Tree, dat: c.Tree): c.Tree = { - q"$thisObj.do_bitSet($off, $dat)($implicitSourceInfo, $implicitCompileOptions)" - } -} - -// Workaround for https://github.com/sbt/sbt/issues/3966 -object InstTransform -// Module instantiation transform -class InstTransform(val c: Context) extends SourceInfoTransformMacro { - import c.universe._ - def apply[T: c.WeakTypeTag](bc: c.Tree): c.Tree = { - q"$thisObj.do_apply($bc)($implicitSourceInfo, $implicitCompileOptions)" - } -} - -// Workaround for https://github.com/sbt/sbt/issues/3966 -object MemTransform -class MemTransform(val c: Context) extends SourceInfoTransformMacro { - import c.universe._ - def apply[T: c.WeakTypeTag](size: c.Tree, t: c.Tree): c.Tree = { - q"$thisObj.do_apply($size, $t)($implicitSourceInfo, $implicitCompileOptions)" - } - def apply_ruw[T: c.WeakTypeTag](size: c.Tree, t: c.Tree, ruw: c.Tree): c.Tree = { - q"$thisObj.do_apply($size, $t, $ruw)($implicitSourceInfo, $implicitCompileOptions)" - } -} - -// Workaround for https://github.com/sbt/sbt/issues/3966 -object MuxTransform -class MuxTransform(val c: Context) extends SourceInfoTransformMacro { - import c.universe._ - def apply[T: c.WeakTypeTag](cond: c.Tree, con: c.Tree, alt: c.Tree): c.Tree = { - val tpe = weakTypeOf[T] - q"$thisObj.do_apply[$tpe]($cond, $con, $alt)($implicitSourceInfo, $implicitCompileOptions)" - } -} - -// Workaround for https://github.com/sbt/sbt/issues/3966 -object VecTransform -class VecTransform(val c: Context) extends SourceInfoTransformMacro { - import c.universe._ - def apply_elts(elts: c.Tree): c.Tree = { - q"$thisObj.do_apply($elts)($implicitSourceInfo, $implicitCompileOptions)" - } - def apply_elt0(elt0: c.Tree, elts: c.Tree*): c.Tree = { - q"$thisObj.do_apply($elt0, ..$elts)($implicitSourceInfo, $implicitCompileOptions)" - } - def tabulate(n: c.Tree)(gen: c.Tree): c.Tree = { - q"$thisObj.do_tabulate($n)($gen)($implicitSourceInfo, $implicitCompileOptions)" - } - def fill(n: c.Tree)(gen: c.Tree): c.Tree = { - q"$thisObj.do_fill($n)($gen)($implicitSourceInfo, $implicitCompileOptions)" - } - def contains(x: c.Tree)(ev: c.Tree): c.Tree = { - q"$thisObj.do_contains($x)($implicitSourceInfo, $ev, $implicitCompileOptions)" - } - def reduceTree(redOp: c.Tree, layerOp: c.Tree): c.Tree = { - q"$thisObj.do_reduceTree($redOp,$layerOp)($implicitSourceInfo, $implicitCompileOptions)" - } - def reduceTreeDefault(redOp: c.Tree ): c.Tree = { - q"$thisObj.do_reduceTree($redOp)($implicitSourceInfo, $implicitCompileOptions)" - } -} - -/** "Automatic" source information transform / insertion macros, which generate the function name - * based on the macro invocation (instead of explicitly writing out every transform). - */ -abstract class AutoSourceTransform extends SourceInfoTransformMacro { - import c.universe._ - /** Returns the TermName of the transformed function, which is the applied function name with do_ - * prepended. - */ - def doFuncTerm: TermName = { - val funcName = c.macroApplication match { - case q"$_.$funcName[..$_](...$_)" => funcName - case _ => throw new Exception(s"Chisel Internal Error: Could not resolve function name from macro application: ${showCode(c.macroApplication)}") // scalastyle:ignore line.size.limit - } - TermName("do_" + funcName) - } -} - -// Workaround for https://github.com/sbt/sbt/issues/3966 -object SourceInfoTransform -class SourceInfoTransform(val c: Context) extends AutoSourceTransform { - import c.universe._ - - def noArg(): c.Tree = { - q"$thisObj.$doFuncTerm($implicitSourceInfo, $implicitCompileOptions)" - } - - def thatArg(that: c.Tree): c.Tree = { - q"$thisObj.$doFuncTerm($that)($implicitSourceInfo, $implicitCompileOptions)" - } - - def nArg(n: c.Tree): c.Tree = { - q"$thisObj.$doFuncTerm($n)($implicitSourceInfo, $implicitCompileOptions)" - } - - def pArg(p: c.Tree): c.Tree = { - q"$thisObj.$doFuncTerm($p)($implicitSourceInfo, $implicitCompileOptions)" - } - - def inArg(in: c.Tree): c.Tree = { - q"$thisObj.$doFuncTerm($in)($implicitSourceInfo, $implicitCompileOptions)" - } - - def xArg(x: c.Tree): c.Tree = { - q"$thisObj.$doFuncTerm($x)($implicitSourceInfo, $implicitCompileOptions)" - } - - def xyArg(x: c.Tree, y: c.Tree): c.Tree = { - q"$thisObj.$doFuncTerm($x, $y)($implicitSourceInfo, $implicitCompileOptions)" - } - - def xEnArg(x: c.Tree, en: c.Tree): c.Tree = { - q"$thisObj.$doFuncTerm($x, $en)($implicitSourceInfo, $implicitCompileOptions)" - } -} - -// Workaround for https://github.com/sbt/sbt/issues/3966 -object CompileOptionsTransform -class CompileOptionsTransform(val c: Context) extends AutoSourceTransform { - import c.universe._ - - def thatArg(that: c.Tree): c.Tree = { - q"$thisObj.$doFuncTerm($that)($implicitCompileOptions)" - } - - def inArg(in: c.Tree): c.Tree = { - q"$thisObj.$doFuncTerm($in)($implicitCompileOptions)" - } - - def pArg(p: c.Tree): c.Tree = { - q"$thisObj.$doFuncTerm($p)($implicitCompileOptions)" - } -} - -// Workaround for https://github.com/sbt/sbt/issues/3966 -object SourceInfoWhiteboxTransform -/** Special whitebox version of the blackbox SourceInfoTransform, used when fun things need to - * happen to satisfy the type system while preventing the use of macro overrides. - */ -class SourceInfoWhiteboxTransform(val c: whitebox.Context) extends AutoSourceTransform { - import c.universe._ - - def noArg(): c.Tree = { - q"$thisObj.$doFuncTerm($implicitSourceInfo, $implicitCompileOptions)" - } - - def thatArg(that: c.Tree): c.Tree = { - q"$thisObj.$doFuncTerm($that)($implicitSourceInfo, $implicitCompileOptions)" - } -} diff --git a/macros/src/main/scala/chisel3/SourceInfoDoc.scala b/macros/src/main/scala/chisel3/SourceInfoDoc.scala new file mode 100644 index 00000000..c44da915 --- /dev/null +++ b/macros/src/main/scala/chisel3/SourceInfoDoc.scala @@ -0,0 +1,38 @@ +// See LICENSE for license details. + +package chisel3 + +/** Provides ScalaDoc information for "hidden" `do_*` methods + * + * Mix this into classes/objects that have `do_*` methods to get access to the shared `SourceInfoTransformMacro` + * ScalaDoc group and the lengthy `groupdesc` below. + * + * @groupdesc SourceInfoTransformMacro + * + *

+ * '''These internal methods are not part of the public-facing API!''' + *
+ *
+ * + * The equivalent public-facing methods do not have the `do_` prefix or have the same name. Use and look at the + * documentation for those. If you want left shift, use `<<`, not `do_<<`. If you want conversion to a + * [[scala.collection.Seq Seq]] of [[Bool]]s look at the `asBools` above, not the one below. Users can safely ignore + * every method in this group!

+ * + * 🐉🐉🐉 '''Here be dragons...''' 🐉🐉🐉 + *
+ *
+ * + * These `do_X` methods are used to enable both implicit passing of SourceInfo and [[chisel3.CompileOptions]] + * while also supporting chained apply methods. In effect all "normal" methods that you, as a user, will use in your + * designs, are converted to their "hidden", `do_*`, via macro transformations. Without using macros here, only one + * of the above wanted behaviors is allowed (implicit passing and chained applies)---the compiler interprets a + * chained apply as an explicit 'implicit' argument and will throw type errors.

+ * + * The "normal", public-facing methods then take no SourceInfo. However, a macro transforms this public-facing method + * into a call to an internal, hidden `do_*` that takes an explicit SourceInfo by inserting an + * `implicitly[SourceInfo]` as the explicit argument.

+ * + * @groupprio SourceInfoTransformMacro 1001 + */ +trait SourceInfoDoc diff --git a/macros/src/main/scala/chisel3/internal/RangeTransform.scala b/macros/src/main/scala/chisel3/internal/RangeTransform.scala new file mode 100644 index 00000000..0fdbff81 --- /dev/null +++ b/macros/src/main/scala/chisel3/internal/RangeTransform.scala @@ -0,0 +1,197 @@ +// See LICENSE for license details. + +// Macro transforms that statically (at compile time) parse range specifiers and emit the raw +// (non-human-friendly) range constructor calls. + +package chisel3.internal + +import scala.language.experimental.macros +import scala.reflect.macros.blackbox +import scala.util.matching.Regex + +// Workaround for https://github.com/sbt/sbt/issues/3966 +object RangeTransform { + val UnspecifiedNumber: Regex = """(\?).*""".r + val IntegerNumber: Regex = """(-?\d+).*""".r + val DecimalNumber: Regex = """(-?\d+\.\d+).*""".r +} + +/** Convert the string to IntervalRange, with unknown, open or closed endpoints and a binary point + * ranges looks like + * range"[0,4].1" range starts at 0 inclusive ends at 4.inclusively with a binary point of 1 + * range"(0,4).1" range starts at 0 exclusive ends at 4.exclusively with a binary point of 1 + * + * the min and max of the range are the actually min and max values, thus the binary point + * becomes a sort of multiplier for the number of bits. + * E.g. range"[0,3].2" will require at least 4 bits two provide the two decimal places + * + * @param c contains the string context to be parsed + */ +//scalastyle:off cyclomatic.complexity method.length +class RangeTransform(val c: blackbox.Context) { + import c.universe._ + def apply(args: c.Tree*): c.Tree = { + val stringTrees = c.prefix.tree match { + case q"$_(scala.StringContext.apply(..$strings))" => strings + case _ => + c.abort( + c.enclosingPosition, + s"Range macro unable to parse StringContext, got: ${showCode(c.prefix.tree)}" + ) + } + val strings = stringTrees.map { + case Literal(Constant(string: String)) => string + case tree => + c.abort( + c.enclosingPosition, + s"Range macro unable to parse StringContext element, got: ${showRaw(tree)}" + ) + } + + var nextStringIndex: Int = 1 + var nextArgIndex: Int = 0 + var currString: String = strings.head + + /** Mutably gets the next numeric value in the range specifier. + */ + def computeNextValue(): c.Tree = { + currString = currString.dropWhile(_ == ' ') // allow whitespace + if (currString.isEmpty) { + if (nextArgIndex >= args.length) { + c.abort(c.enclosingPosition, s"Incomplete range specifier") + } + val nextArg = args(nextArgIndex) + nextArgIndex += 1 + + if (nextStringIndex >= strings.length) { + c.abort(c.enclosingPosition, s"Incomplete range specifier") + } + currString = strings(nextStringIndex) + nextStringIndex += 1 + + nextArg + } else { + val nextStringVal = currString match { + case RangeTransform.DecimalNumber(numberString) => numberString + case RangeTransform.IntegerNumber(numberString) => numberString + case RangeTransform.UnspecifiedNumber(_) => "?" + case _ => + c.abort( + c.enclosingPosition, + s"Bad number or unspecified bound $currString" + ) + } + currString = currString.substring(nextStringVal.length) + + if (nextStringVal == "?") { + Literal(Constant("?")) + } else { + c.parse(nextStringVal) + } + } + } + + // Currently, not allowed to have the end stops (inclusive / exclusive) be interpolated. + currString = currString.dropWhile(_ == ' ') + val startInclusive = currString.headOption match { + case Some('[') => true + case Some('(') => false + case Some('?') => + c.abort( + c.enclosingPosition, + s"start of range as unknown s must be '[?' or '(?' not '?'" + ) + case Some(other) => + c.abort( + c.enclosingPosition, + s"Unknown start inclusive/exclusive specifier, got: '$other'" + ) + case None => + c.abort( + c.enclosingPosition, + s"No initial inclusive/exclusive specifier" + ) + } + + currString = currString.substring(1) // eat the inclusive/exclusive specifier + val minArg = computeNextValue() + currString = currString.dropWhile(_ == ' ') + if (currString(0) != ',') { + c.abort(c.enclosingPosition, s"Incomplete range specifier, expected ','") + } + if (currString.head != ',') { + c.abort( + c.enclosingPosition, + s"Incomplete range specifier, expected ',', got $currString" + ) + } + + currString = currString.substring(1) // eat the comma + + val maxArg = computeNextValue() + currString = currString.dropWhile(_ == ' ') + + val endInclusive = currString.headOption match { + case Some(']') => true + case Some(')') => false + case Some('?') => + c.abort( + c.enclosingPosition, + s"start of range as unknown s must be '[?' or '(?' not '?'" + ) + case Some(other) => + c.abort( + c.enclosingPosition, + s"Unknown end inclusive/exclusive specifier, got: '$other' expecting ')' or ']'" + ) + case None => + c.abort( + c.enclosingPosition, + s"Incomplete range specifier, missing end inclusive/exclusive specifier" + ) + } + currString = currString.substring(1) // eat the inclusive/exclusive specifier + currString = currString.dropWhile(_ == ' ') + + val binaryPointString = currString.headOption match { + case Some('.') => + currString = currString.substring(1) + computeNextValue() + case Some(other) => + c.abort( + c.enclosingPosition, + s"Unknown end binary point prefix, got: '$other' was expecting '.'" + ) + case None => + Literal(Constant(0)) + } + + if (nextArgIndex < args.length) { + val unused = args.mkString("") + c.abort( + c.enclosingPosition, + s"Unused interpolated values in range specifier: '$unused'" + ) + } + if (!currString.isEmpty || nextStringIndex < strings.length) { + val unused = currString + strings + .slice(nextStringIndex, strings.length) + .mkString(", ") + c.abort( + c.enclosingPosition, + s"Unused characters in range specifier: '$unused'" + ) + } + + val startBound = + q"_root_.chisel3.internal.firrtl.IntervalRange.getBound($startInclusive, $minArg)" + + val endBound = + q"_root_.chisel3.internal.firrtl.IntervalRange.getBound($endInclusive, $maxArg)" + + val binaryPoint = + q"_root_.chisel3.internal.firrtl.IntervalRange.getBinaryPoint($binaryPointString)" + + q"_root_.chisel3.internal.firrtl.IntervalRange($startBound, $endBound, $binaryPoint)" + } +} diff --git a/macros/src/main/scala/chisel3/internal/RuntimeDeprecationTransform.scala b/macros/src/main/scala/chisel3/internal/RuntimeDeprecationTransform.scala new file mode 100644 index 00000000..e1f528b3 --- /dev/null +++ b/macros/src/main/scala/chisel3/internal/RuntimeDeprecationTransform.scala @@ -0,0 +1,42 @@ +// See LICENSE for license details. + +package chisel3.internal + +import scala.reflect.macros.whitebox.Context +import scala.annotation.{StaticAnnotation, compileTimeOnly} +import scala.language.experimental.macros + +// Workaround for https://github.com/sbt/sbt/issues/3966 +object RuntimeDeprecatedTransform +class RuntimeDeprecatedTransform(val c: Context) { + import c.universe._ + + /** Adds a Builder.deprecated(...) call based on the contents of a plain @deprecated annotation. + */ + def runtimeDeprecated(annottees: c.Tree*): c.Tree = { + val transformed = annottees.map(annottee => annottee match { + case q"$mods def $tname[..$tparams](...$paramss): $tpt = $expr" => { + val Modifiers(_, _, annotations) = mods + val annotationMessage = annotations.collect { // get all messages from deprecated annotations + case q"new deprecated($desc, $since)" => desc + } match { // ensure there's only one and return it + case msg :: Nil => msg + case _ => c.abort(c.enclosingPosition, s"@chiselRuntimeDeprecated annotion must be used with exactly one @deprecated annotation, got annotations $annotations") // scalastyle:ignore line.size.limit + } + val message = s"$tname is deprecated: $annotationMessage" + val transformedExpr = q""" { + _root_.chisel3.internal.Builder.deprecated($message) + $expr + } """ + q"$mods def $tname[..$tparams](...$paramss): $tpt = $transformedExpr" + } + case other => c.abort(c.enclosingPosition, s"@chiselRuntimeDeprecated annotion may only be used on defs, got ${showCode(other)}") // scalastyle:ignore line.size.limit + }) + q"..$transformed" + } +} + +@compileTimeOnly("enable macro paradise to expand macro annotations") +class chiselRuntimeDeprecated extends StaticAnnotation { + def macroTransform(annottees: Any*): Any = macro chisel3.internal.RuntimeDeprecatedTransform.runtimeDeprecated +} diff --git a/macros/src/main/scala/chisel3/internal/naming/NamingAnnotations.scala b/macros/src/main/scala/chisel3/internal/naming/NamingAnnotations.scala new file mode 100644 index 00000000..bf4879ec --- /dev/null +++ b/macros/src/main/scala/chisel3/internal/naming/NamingAnnotations.scala @@ -0,0 +1,205 @@ +// See LICENSE for license details. + +// Transform implementations for name-propagation related annotations. +// +// Helpful references: +// http://docs.scala-lang.org/overviews/quasiquotes/syntax-summary.html#definitions +// for quasiquote structures of various Scala structures +// http://jsuereth.com/scala/2009/02/05/leveraging-annotations-in-scala.html +// use of Transformer +// http://www.scala-lang.org/old/sites/default/files/sids/rytz/Wed,%202010-01-27,%2015:10/annots.pdf +// general annotations reference + +package chisel3.internal.naming + +import scala.reflect.macros.whitebox.Context +import scala.annotation.{StaticAnnotation, compileTimeOnly} +import scala.language.experimental.macros + +// Workaround for https://github.com/sbt/sbt/issues/3966 +object DebugTransforms +class DebugTransforms(val c: Context) { + import c.universe._ + + /** Passthrough transform that prints the annottee for debugging purposes. + * No guarantees are made on what this annotation does, and it may very well change over time. + * + * The print is warning level to make it visually easier to spot, as well as a reminder that + * this annotation should not make it to production / committed code. + */ + def dump(annottees: c.Tree*): c.Tree = { + val combined = annottees.map({ tree => show(tree) }).mkString("\r\n\r\n") + annottees.foreach(tree => c.warning(c.enclosingPosition, s"Debug dump:\n$combined")) + q"..$annottees" + } + + /** Passthrough transform that prints the annottee as a tree for debugging purposes. + * No guarantees are made on what this annotation does, and it may very well change over time. + * + * The print is warning level to make it visually easier to spot, as well as a reminder that + * this annotation should not make it to production / committed code. + */ + def treedump(annottees: c.Tree*): c.Tree = { + val combined = annottees.map({ tree => showRaw(tree) }).mkString("\r\n") + annottees.foreach(tree => c.warning(c.enclosingPosition, s"Debug tree dump:\n$combined")) + q"..$annottees" + } +} + +// Workaround for https://github.com/sbt/sbt/issues/3966 +object NamingTransforms +class NamingTransforms(val c: Context) { + import c.universe._ + + val globalNamingStack = q"_root_.chisel3.internal.DynamicNamingStack" + + /** Base transformer that provides the val name transform. + * Should not be instantiated, since by default this will recurse everywhere and break the + * naming context variable bounds. + */ + trait ValNameTransformer extends Transformer { + val contextVar: TermName + + override def transform(tree: Tree): Tree = tree match { + // Intentionally not prefixed with $mods, since modifiers usually mean the val definition + // is in a non-transformable location, like as a parameter list. + // TODO: is this exhaustive / correct in all cases? + case q"val $tname: $tpt = $expr" => { + val TermName(tnameStr: String) = tname + val transformedExpr = super.transform(expr) + q"val $tname: $tpt = $contextVar.name($transformedExpr, $tnameStr)" + } + case other => super.transform(other) + } + } + + /** Module-specific val name transform, containing logic to prevent from recursing into inner + * classes and applies the naming transform on inner functions. + */ + class ClassBodyTransformer(val contextVar: TermName) extends ValNameTransformer { + override def transform(tree: Tree): Tree = tree match { + case q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }" => // scalastyle:ignore line.size.limit + tree // don't recurse into inner classes + case q"$mods trait $tpname[..$tparams] extends { ..$earlydefns } with ..$parents { $self => ..$stats }" => + tree // don't recurse into inner classes + case q"$mods def $tname[..$tparams](...$paramss): $tpt = $expr" => { + val Modifiers(_, _, annotations) = mods + // don't apply naming transform twice + val containsChiselName = annotations.map({q"new chiselName()" equalsStructure _}).fold(false)({_||_}) + // transforming overloaded initializers causes errors, and the transform isn't helpful + val isInitializer = tname == TermName("") + if (containsChiselName || isInitializer) { + tree + } else { + // apply chiselName transform by default + val transformedExpr = transformHierarchicalMethod(expr) + q"$mods def $tname[..$tparams](...$paramss): $tpt = $transformedExpr" + } + } + case other => super.transform(other) + } + } + + /** Method-specific val name transform, handling the return case. + */ + class MethodTransformer(val contextVar: TermName) extends ValNameTransformer { + override def transform(tree: Tree): Tree = tree match { + // TODO: better error messages when returning nothing + case q"return $expr" => q"return $globalNamingStack.popReturnContext($expr, $contextVar)" + // Do not recurse into methods + case q"$mods def $tname[..$tparams](...$paramss): $tpt = $expr" => tree + case other => super.transform(other) + } + } + + /** Applies the val name transform to a class body. + * Closes context on top level or return local context to englobing context. + * Closing context only makes sense when top level a Module. + * A Module is always the naming top level. + * Transformed classes can be either Module or standard class. + */ + def transformClassBody(stats: List[c.Tree]): Tree = { + val contextVar = TermName(c.freshName("namingContext")) + val transformedBody = (new ClassBodyTransformer(contextVar)).transformTrees(stats) + // Note: passing "this" to popReturnContext is mandatory for propagation through non-module classes + q""" + val $contextVar = $globalNamingStack.pushContext() + ..$transformedBody + if($globalNamingStack.length == 1){ + $contextVar.namePrefix("") + } + $globalNamingStack.popReturnContext(this, $contextVar) + """ + } + + /** Applies the val name transform to a method body, doing additional bookkeeping with the + * context to allow names to propagate and prefix through the function call stack. + */ + def transformHierarchicalMethod(expr: c.Tree): Tree = { + val contextVar = TermName(c.freshName("namingContext")) + val transformedBody = (new MethodTransformer(contextVar)).transform(expr) + + q"""{ + val $contextVar = $globalNamingStack.pushContext() + $globalNamingStack.popReturnContext($transformedBody, $contextVar) + } + """ + } + + /** Applies naming transforms to vals in the annotated module or method. + * + * For methods, a hierarchical naming transform is used, where it will try to give objects names + * based on the call stack, assuming all functions on the stack are annotated as such and return + * a non-AnyVal object. Does not recurse into inner functions. + * + * For modules, this serves as the root of the call stack hierarchy for naming purposes. Methods + * will have chiselName annotations (non-recursively), but this does NOT affect inner classes. + * + * Basically rewrites all instances of: + * val name = expr + * to: + * val name = context.name(expr, name) + */ + def chiselName(annottees: c.Tree*): c.Tree = { + var namedElts: Int = 0 + + val transformed = annottees.map(annottee => annottee match { + // scalastyle:off line.size.limit + case q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }" => { + val transformedStats = transformClassBody(stats) + namedElts += 1 + q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$transformedStats }" + } + case q"$mods object $tname extends { ..$earlydefns } with ..$parents { $self => ..$body }" => { + annottee // Don't fail noisly when a companion object is passed in with the actual class def + } + // Currently disallow on traits, this won't work well with inheritance. + case q"$mods def $tname[..$tparams](...$paramss): $tpt = $expr" => { + val transformedExpr = transformHierarchicalMethod(expr) + namedElts += 1 + q"$mods def $tname[..$tparams](...$paramss): $tpt = $transformedExpr" + } + case other => c.abort(c.enclosingPosition, s"@chiselName annotion may only be used on classes and methods, got ${showCode(other)}") + }) + + if (namedElts != 1) { + c.abort(c.enclosingPosition, s"@chiselName annotation did not match exactly one valid tree, got:\r\n${annottees.map(tree => showCode(tree)).mkString("\r\n\r\n")}") + } + // scalastyle:on line.size.limit + + q"..$transformed" + } +} + +@compileTimeOnly("enable macro paradise to expand macro annotations") +class dump extends StaticAnnotation { // scalastyle:ignore class.name + def macroTransform(annottees: Any*): Any = macro chisel3.internal.naming.DebugTransforms.dump +} +@compileTimeOnly("enable macro paradise to expand macro annotations") +class treedump extends StaticAnnotation { // scalastyle:ignore class.name + def macroTransform(annottees: Any*): Any = macro chisel3.internal.naming.DebugTransforms.treedump +} +@compileTimeOnly("enable macro paradise to expand macro annotations") +class chiselName extends StaticAnnotation { // scalastyle:ignore class.name + def macroTransform(annottees: Any*): Any = macro chisel3.internal.naming.NamingTransforms.chiselName +} diff --git a/macros/src/main/scala/chisel3/internal/sourceinfo/SourceInfoTransform.scala b/macros/src/main/scala/chisel3/internal/sourceinfo/SourceInfoTransform.scala new file mode 100644 index 00000000..6d7c3411 --- /dev/null +++ b/macros/src/main/scala/chisel3/internal/sourceinfo/SourceInfoTransform.scala @@ -0,0 +1,186 @@ +// See LICENSE for license details. + +// This file transform macro definitions to explicitly add implicit source info to Chisel method +// calls. + +package chisel3.internal.sourceinfo + +import scala.language.experimental.macros +import scala.reflect.macros.blackbox.Context +import scala.reflect.macros.whitebox + + +/** Transforms a function call so that it can both provide implicit-style source information and + * have a chained apply call. Without macros, only one is possible, since having a implicit + * argument in the definition will cause the compiler to interpret a chained apply as an + * explicit implicit argument and give type errors. + * + * Instead of an implicit argument, the public-facing function no longer takes a SourceInfo at all. + * The macro transforms the public-facing function into a call to an internal function that takes + * an explicit SourceInfo by inserting an implicitly[SourceInfo] as the explicit argument. + */ +trait SourceInfoTransformMacro { + val c: Context + import c.universe._ + def thisObj: Tree = c.prefix.tree + def implicitSourceInfo = q"implicitly[_root_.chisel3.internal.sourceinfo.SourceInfo]" + def implicitCompileOptions = q"implicitly[_root_.chisel3.CompileOptions]" +} + +// Workaround for https://github.com/sbt/sbt/issues/3966 +object UIntTransform +class UIntTransform(val c: Context) extends SourceInfoTransformMacro { + import c.universe._ + def bitset(off: c.Tree, dat: c.Tree): c.Tree = { + q"$thisObj.do_bitSet($off, $dat)($implicitSourceInfo, $implicitCompileOptions)" + } +} + +// Workaround for https://github.com/sbt/sbt/issues/3966 +object InstTransform +// Module instantiation transform +class InstTransform(val c: Context) extends SourceInfoTransformMacro { + import c.universe._ + def apply[T: c.WeakTypeTag](bc: c.Tree): c.Tree = { + q"$thisObj.do_apply($bc)($implicitSourceInfo, $implicitCompileOptions)" + } +} + +// Workaround for https://github.com/sbt/sbt/issues/3966 +object MemTransform +class MemTransform(val c: Context) extends SourceInfoTransformMacro { + import c.universe._ + def apply[T: c.WeakTypeTag](size: c.Tree, t: c.Tree): c.Tree = { + q"$thisObj.do_apply($size, $t)($implicitSourceInfo, $implicitCompileOptions)" + } + def apply_ruw[T: c.WeakTypeTag](size: c.Tree, t: c.Tree, ruw: c.Tree): c.Tree = { + q"$thisObj.do_apply($size, $t, $ruw)($implicitSourceInfo, $implicitCompileOptions)" + } +} + +// Workaround for https://github.com/sbt/sbt/issues/3966 +object MuxTransform +class MuxTransform(val c: Context) extends SourceInfoTransformMacro { + import c.universe._ + def apply[T: c.WeakTypeTag](cond: c.Tree, con: c.Tree, alt: c.Tree): c.Tree = { + val tpe = weakTypeOf[T] + q"$thisObj.do_apply[$tpe]($cond, $con, $alt)($implicitSourceInfo, $implicitCompileOptions)" + } +} + +// Workaround for https://github.com/sbt/sbt/issues/3966 +object VecTransform +class VecTransform(val c: Context) extends SourceInfoTransformMacro { + import c.universe._ + def apply_elts(elts: c.Tree): c.Tree = { + q"$thisObj.do_apply($elts)($implicitSourceInfo, $implicitCompileOptions)" + } + def apply_elt0(elt0: c.Tree, elts: c.Tree*): c.Tree = { + q"$thisObj.do_apply($elt0, ..$elts)($implicitSourceInfo, $implicitCompileOptions)" + } + def tabulate(n: c.Tree)(gen: c.Tree): c.Tree = { + q"$thisObj.do_tabulate($n)($gen)($implicitSourceInfo, $implicitCompileOptions)" + } + def fill(n: c.Tree)(gen: c.Tree): c.Tree = { + q"$thisObj.do_fill($n)($gen)($implicitSourceInfo, $implicitCompileOptions)" + } + def contains(x: c.Tree)(ev: c.Tree): c.Tree = { + q"$thisObj.do_contains($x)($implicitSourceInfo, $ev, $implicitCompileOptions)" + } + def reduceTree(redOp: c.Tree, layerOp: c.Tree): c.Tree = { + q"$thisObj.do_reduceTree($redOp,$layerOp)($implicitSourceInfo, $implicitCompileOptions)" + } + def reduceTreeDefault(redOp: c.Tree ): c.Tree = { + q"$thisObj.do_reduceTree($redOp)($implicitSourceInfo, $implicitCompileOptions)" + } +} + +/** "Automatic" source information transform / insertion macros, which generate the function name + * based on the macro invocation (instead of explicitly writing out every transform). + */ +abstract class AutoSourceTransform extends SourceInfoTransformMacro { + import c.universe._ + /** Returns the TermName of the transformed function, which is the applied function name with do_ + * prepended. + */ + def doFuncTerm: TermName = { + val funcName = c.macroApplication match { + case q"$_.$funcName[..$_](...$_)" => funcName + case _ => throw new Exception(s"Chisel Internal Error: Could not resolve function name from macro application: ${showCode(c.macroApplication)}") // scalastyle:ignore line.size.limit + } + TermName("do_" + funcName) + } +} + +// Workaround for https://github.com/sbt/sbt/issues/3966 +object SourceInfoTransform +class SourceInfoTransform(val c: Context) extends AutoSourceTransform { + import c.universe._ + + def noArg(): c.Tree = { + q"$thisObj.$doFuncTerm($implicitSourceInfo, $implicitCompileOptions)" + } + + def thatArg(that: c.Tree): c.Tree = { + q"$thisObj.$doFuncTerm($that)($implicitSourceInfo, $implicitCompileOptions)" + } + + def nArg(n: c.Tree): c.Tree = { + q"$thisObj.$doFuncTerm($n)($implicitSourceInfo, $implicitCompileOptions)" + } + + def pArg(p: c.Tree): c.Tree = { + q"$thisObj.$doFuncTerm($p)($implicitSourceInfo, $implicitCompileOptions)" + } + + def inArg(in: c.Tree): c.Tree = { + q"$thisObj.$doFuncTerm($in)($implicitSourceInfo, $implicitCompileOptions)" + } + + def xArg(x: c.Tree): c.Tree = { + q"$thisObj.$doFuncTerm($x)($implicitSourceInfo, $implicitCompileOptions)" + } + + def xyArg(x: c.Tree, y: c.Tree): c.Tree = { + q"$thisObj.$doFuncTerm($x, $y)($implicitSourceInfo, $implicitCompileOptions)" + } + + def xEnArg(x: c.Tree, en: c.Tree): c.Tree = { + q"$thisObj.$doFuncTerm($x, $en)($implicitSourceInfo, $implicitCompileOptions)" + } +} + +// Workaround for https://github.com/sbt/sbt/issues/3966 +object CompileOptionsTransform +class CompileOptionsTransform(val c: Context) extends AutoSourceTransform { + import c.universe._ + + def thatArg(that: c.Tree): c.Tree = { + q"$thisObj.$doFuncTerm($that)($implicitCompileOptions)" + } + + def inArg(in: c.Tree): c.Tree = { + q"$thisObj.$doFuncTerm($in)($implicitCompileOptions)" + } + + def pArg(p: c.Tree): c.Tree = { + q"$thisObj.$doFuncTerm($p)($implicitCompileOptions)" + } +} + +// Workaround for https://github.com/sbt/sbt/issues/3966 +object SourceInfoWhiteboxTransform +/** Special whitebox version of the blackbox SourceInfoTransform, used when fun things need to + * happen to satisfy the type system while preventing the use of macro overrides. + */ +class SourceInfoWhiteboxTransform(val c: whitebox.Context) extends AutoSourceTransform { + import c.universe._ + + def noArg(): c.Tree = { + q"$thisObj.$doFuncTerm($implicitSourceInfo, $implicitCompileOptions)" + } + + def thatArg(that: c.Tree): c.Tree = { + q"$thisObj.$doFuncTerm($that)($implicitSourceInfo, $implicitCompileOptions)" + } +} -- cgit v1.2.3