diff options
Diffstat (limited to 'core')
| -rw-r--r-- | core/src/main/scala/chisel3/Aggregate.scala | 61 | ||||
| -rw-r--r-- | core/src/main/scala/chisel3/BlackBox.scala | 21 | ||||
| -rw-r--r-- | core/src/main/scala/chisel3/Data.scala | 2 | ||||
| -rw-r--r-- | core/src/main/scala/chisel3/Mem.scala | 164 | ||||
| -rw-r--r-- | core/src/main/scala/chisel3/RawModule.scala | 39 | ||||
| -rw-r--r-- | core/src/main/scala/chisel3/experimental/hierarchy/Definition.scala | 2 | ||||
| -rw-r--r-- | core/src/main/scala/chisel3/internal/Builder.scala | 7 | ||||
| -rw-r--r-- | core/src/main/scala/chisel3/internal/Error.scala | 72 |
8 files changed, 288 insertions, 80 deletions
diff --git a/core/src/main/scala/chisel3/Aggregate.scala b/core/src/main/scala/chisel3/Aggregate.scala index 15cdf428..06ae36f3 100644 --- a/core/src/main/scala/chisel3/Aggregate.scala +++ b/core/src/main/scala/chisel3/Aggregate.scala @@ -1207,7 +1207,66 @@ abstract class Bundle(implicit compileOptions: CompileOptions) extends Record { * assert(uint === "h12345678".U) // This will pass * }}} */ - final lazy val elements: SeqMap[String, Data] = { + final lazy val elements: SeqMap[String, Data] = + // _elementsImpl is a method, only call it once + _elementsImpl match { + // Those not using plugin-generated _elementsImpl use the old reflective implementation + case oldElements: VectorMap[_, _] => oldElements.asInstanceOf[VectorMap[String, Data]] + // Plugin-generated _elementsImpl are incomplete and need some processing + case rawElements => _processRawElements(rawElements) + } + + // The compiler plugin is imperfect at picking out elements statically so we process at runtime + // checking for errors and filtering out mistakes + private def _processRawElements(rawElements: Iterable[(String, Any)]): SeqMap[String, Data] = { + val hardwareFields = rawElements.flatMap { + case (name, data: Data) => + if (data.isSynthesizable) { + Some(s"$name: $data") + } else { + None + } + case (name, Some(data: Data)) => + if (data.isSynthesizable) { + Some(s"$name: $data") + } else { + None + } + case (name, 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 '${name}'). " + + "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 + } + None + + case _ => None + } + if (hardwareFields.nonEmpty) { + throw ExpectedChiselTypeException(s"Bundle: $this contains hardware fields: " + hardwareFields.mkString(",")) + } + VectorMap(rawElements.toSeq.flatMap { + case (name, data: Data) => + Some(name -> data) + case (name, Some(data: Data)) => + Some(name -> data) + case _ => None + }.sortWith { + case ((an, a), (bn, b)) => (a._id > b._id) || ((a eq b) && (an > bn)) + }: _*) + } + + /* The old, reflective implementation of Bundle.elements + * This method is optionally overwritten by the compiler plugin for much better performance + */ + protected def _elementsImpl: Iterable[(String, Any)] = { val nameMap = LinkedHashMap[String, Data]() for (m <- getPublicFields(classOf[Bundle])) { getBundleField(m) match { diff --git a/core/src/main/scala/chisel3/BlackBox.scala b/core/src/main/scala/chisel3/BlackBox.scala index 89c4ccd3..2f04fb2e 100644 --- a/core/src/main/scala/chisel3/BlackBox.scala +++ b/core/src/main/scala/chisel3/BlackBox.scala @@ -145,27 +145,30 @@ abstract class BlackBox( extends BaseBlackBox { // Find a Record port named "io" for purposes of stripping the prefix - private[chisel3] lazy val _io: Record = + private[chisel3] lazy val _io: Option[Record] = this .findPort("io") .collect { case r: Record => r } // Must be a Record - .getOrElse(null) // null handling occurs in generateComponent // Allow access to bindings from the compatibility package - protected def _compatIoPortBound() = portsContains(_io) + protected def _compatIoPortBound() = _io.exists(portsContains(_)) private[chisel3] override def generateComponent(): Option[Component] = { _compatAutoWrapPorts() // pre-IO(...) compatibility hack // Restrict IO to just io, clock, and reset - require(_io != null, "BlackBox must have a port named 'io' of type Record!") - require(portsContains(_io), "BlackBox must have io wrapped in IO(...)") + if (!_io.forall(portsContains)) { + throwException(s"BlackBox '$this' must have a port named 'io' of type Record wrapped in IO(...)!") + } + require(portsSize == 1, "BlackBox must only have one IO, called `io`") require(!_closed, "Can't generate module more than once") _closed = true - val namedPorts = _io.elements.toSeq.reverse // ListMaps are stored in reverse order + val io = _io.get + + val namedPorts = io.elements.toSeq.reverse // ListMaps are stored in reverse order // 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 @@ -181,18 +184,18 @@ abstract class BlackBox( // 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) { + 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) + val component = DefBlackBox(this, name, firrtlPorts, io.specifiedDirection, params) _component = Some(component) _component } private[chisel3] def initializeInParent(parentCompileOptions: CompileOptions): Unit = { - for ((_, port) <- _io.elements) { + for ((_, port) <- _io.map(_.elements).getOrElse(Nil)) { pushCommand(DefInvalid(UnlocatableSourceInfo, port.ref)) } } diff --git a/core/src/main/scala/chisel3/Data.scala b/core/src/main/scala/chisel3/Data.scala index 9e9f5dbb..ef43a3b0 100644 --- a/core/src/main/scala/chisel3/Data.scala +++ b/core/src/main/scala/chisel3/Data.scala @@ -480,7 +480,7 @@ abstract class Data extends HasId with NamedComponent with SourceInfoDoc { protected[chisel3] def binding: Option[Binding] = _binding protected def binding_=(target: Binding) { if (_binding.isDefined) { - throw RebindingException(s"Attempted reassignment of binding to $this") + throw RebindingException(s"Attempted reassignment of binding to $this, from: ${target}") } _binding = Some(target) } diff --git a/core/src/main/scala/chisel3/Mem.scala b/core/src/main/scala/chisel3/Mem.scala index ff5072ad..3f37308c 100644 --- a/core/src/main/scala/chisel3/Mem.scala +++ b/core/src/main/scala/chisel3/Mem.scala @@ -9,7 +9,7 @@ import firrtl.{ir => fir} import chisel3.internal._ import chisel3.internal.Builder.pushCommand import chisel3.internal.firrtl._ -import chisel3.internal.sourceinfo.{MemTransform, SourceInfo, SourceInfoTransform, UnlocatableSourceInfo} +import chisel3.internal.sourceinfo.{MemTransform, SourceInfo, SourceInfoTransform, SourceLine, UnlocatableSourceInfo} object Mem { @@ -56,6 +56,17 @@ sealed abstract class MemBase[T <: Data](val t: T, val length: BigInt) with SourceInfoDoc { _parent.foreach(_.addId(this)) + private val clockInst: Clock = Builder.forcedClock + + protected def clockWarning(sourceInfo: Option[SourceInfo]): Unit = { + // Turn into pretty String if possible, if not, Builder.deprecated will find one via stack trace + val infoStr = sourceInfo.collect { case SourceLine(file, line, col) => s"$file:$line:$col" } + Builder.deprecated( + "The clock used to initialize the memory is different than the one used to initialize the port. " + + "If this is intentional, please pass the clock explicitly when creating the port. This behavior will be an error in 3.6.0", + infoStr + ) + } // REVIEW TODO: make accessors (static/dynamic, read/write) combinations consistent. /** Creates a read accessor into the memory with static addressing. See the @@ -63,17 +74,17 @@ sealed abstract class MemBase[T <: Data](val t: T, val length: BigInt) */ 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) + apply(idx.asUInt, Builder.forcedClock) } + /** 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: Int)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = do_apply(BigInt(idx))(sourceInfo, compileOptions) @@ -85,7 +96,12 @@ sealed abstract class MemBase[T <: Data](val t: T, val length: BigInt) /** @group SourceInfoTransformMacro */ def do_apply(idx: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = - makePort(sourceInfo, idx, MemPortDirection.INFER) + do_apply_impl(idx, Builder.forcedClock, MemPortDirection.INFER, true) + + def apply(x: UInt, y: Clock): T = macro SourceInfoTransform.xyArg + + def do_apply(idx: UInt, clock: Clock)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = + do_apply_impl(idx, clock, MemPortDirection.INFER, false) /** Creates a read accessor into the memory with dynamic addressing. See the * class documentation of the memory for more detailed information. @@ -94,16 +110,65 @@ sealed abstract class MemBase[T <: Data](val t: T, val length: BigInt) /** @group SourceInfoTransformMacro */ def do_read(idx: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = - makePort(sourceInfo, idx, MemPortDirection.READ) + do_apply_impl(idx, Builder.forcedClock, MemPortDirection.READ, true) + + /** Creates a read accessor into the memory with dynamic addressing. + * Takes a clock parameter to bind a clock that may be different + * from the implicit clock. See the class documentation of the memory + * for more detailed information. + */ + def read(x: UInt, y: Clock): T = macro SourceInfoTransform.xyArg + + /** @group SourceInfoTransformMacro */ + def do_read(idx: UInt, clock: Clock)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = + do_apply_impl(idx, clock, MemPortDirection.READ, false) + + protected def do_apply_impl( + idx: UInt, + clock: Clock, + dir: MemPortDirection, + warn: Boolean + )( + implicit sourceInfo: SourceInfo, + compileOptions: CompileOptions + ): T = { + if (warn && clock != clockInst) { + clockWarning(Some(sourceInfo)) + } + makePort(sourceInfo, idx, dir, clock) + } /** 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 = { + def write(idx: UInt, data: T)(implicit compileOptions: CompileOptions): Unit = + write_impl(idx, data, Builder.forcedClock, true) + + /** Creates a write accessor into the memory with a clock + * that may be different from the implicit clock. + * + * @param idx memory element index to write into + * @param data new data to write + * @param clock clock to bind to this accessor + */ + def write(idx: UInt, data: T, clock: Clock)(implicit compileOptions: CompileOptions): Unit = + write_impl(idx, data, clock, false) + + private def write_impl( + idx: UInt, + data: T, + clock: Clock, + warn: Boolean + )( + implicit compileOptions: CompileOptions + ): Unit = { + if (warn && clock != clockInst) { + clockWarning(None) + } implicit val sourceInfo = UnlocatableSourceInfo - makePort(UnlocatableSourceInfo, idx, MemPortDirection.WRITE) := data + makePort(UnlocatableSourceInfo, idx, MemPortDirection.WRITE, clock) := data } /** Creates a masked write accessor into the memory. @@ -122,9 +187,46 @@ sealed abstract class MemBase[T <: Data](val t: T, val length: BigInt) )( implicit evidence: T <:< Vec[_], compileOptions: CompileOptions + ): Unit = + masked_write_impl(idx, data, mask, Builder.forcedClock, true) + + /** Creates a masked write accessor into the memory with a clock + * that may be different from the implicit clock. + * + * @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. + * @param clock clock to bind to this accessor + * + * @note this is only allowed if the memory's element data type is a Vec + */ + def write( + idx: UInt, + data: T, + mask: Seq[Bool], + clock: Clock + )( + implicit evidence: T <:< Vec[_], + compileOptions: CompileOptions + ): Unit = + masked_write_impl(idx, data, mask, clock, false) + + private def masked_write_impl( + idx: UInt, + data: T, + mask: Seq[Bool], + clock: Clock, + warn: Boolean + )( + implicit evidence: T <:< Vec[_], + compileOptions: CompileOptions ): Unit = { implicit val sourceInfo = UnlocatableSourceInfo - val accessor = makePort(sourceInfo, idx, MemPortDirection.WRITE).asInstanceOf[Vec[Data]] + if (warn && clock != clockInst) { + clockWarning(None) + } + val accessor = makePort(sourceInfo, idx, MemPortDirection.WRITE, clock).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})") @@ -139,7 +241,8 @@ sealed abstract class MemBase[T <: Data](val t: T, val length: BigInt) private def makePort( sourceInfo: SourceInfo, idx: UInt, - dir: MemPortDirection + dir: MemPortDirection, + clock: Clock )( implicit compileOptions: CompileOptions ): T = { @@ -147,7 +250,7 @@ sealed abstract class MemBase[T <: Data](val t: T, val length: BigInt) val i = Vec.truncateIndex(idx, length)(sourceInfo, compileOptions) val port = pushCommand( - DefMemPort(sourceInfo, t.cloneTypeFull, Node(this), dir, i.ref, Builder.forcedClock.ref) + DefMemPort(sourceInfo, t.cloneTypeFull, Node(this), dir, i.ref, clock.ref) ).id // Bind each element of port to being a MemoryPort port.bind(MemoryPortBinding(Builder.forcedUserModule, Builder.currentWhen)) @@ -244,23 +347,44 @@ object SyncReadMem { */ sealed class SyncReadMem[T <: Data] private (t: T, n: BigInt, val readUnderWrite: SyncReadMem.ReadUnderWrite) extends MemBase[T](t, n) { + + override def read(x: UInt): T = macro SourceInfoTransform.xArg + + /** @group SourceInfoTransformMacro */ + override def do_read(idx: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = + do_read(idx = idx, en = true.B) + 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 = { + def do_read(idx: UInt, en: Bool)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = + _read_impl(idx, en, Builder.forcedClock, true) + + def read(idx: UInt, en: Bool, clock: Clock): T = macro SourceInfoTransform.xyzArg + + /** @group SourceInfoTransformMacro */ + def do_read(idx: UInt, en: Bool, clock: Clock)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = + _read_impl(idx, en, clock, false) + + /** @group SourceInfoTransformMacro */ + private def _read_impl( + addr: UInt, + enable: Bool, + clock: Clock, + warn: Boolean + )( + implicit sourceInfo: SourceInfo, + compileOptions: CompileOptions + ): T = { val a = Wire(UInt()) a := DontCare var port: Option[T] = None when(enable) { a := addr - port = Some(super.do_read(a)) + port = Some(super.do_apply_impl(a, clock, MemPortDirection.READ, warn)) } port.get } - - /** @group SourceInfoTransformMacro */ - override def do_read(idx: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions) = - do_read(addr = idx, enable = true.B) // note: we implement do_read(addr) for SyncReadMem in terms of do_read(addr, en) in order to ensure that // `mem.read(addr)` will always behave the same as `mem.read(addr, true.B)` } diff --git a/core/src/main/scala/chisel3/RawModule.scala b/core/src/main/scala/chisel3/RawModule.scala index b81372d8..12b6a76a 100644 --- a/core/src/main/scala/chisel3/RawModule.scala +++ b/core/src/main/scala/chisel3/RawModule.scala @@ -178,11 +178,12 @@ package object internal { // Private reflective version of "val io" to maintain Chisel.Module semantics without having // io as a virtual method. See https://github.com/freechipsproject/chisel3/pull/1550 for more // information about the removal of "val io" - private def reflectivelyFindValIO(self: BaseModule): Record = { + private def reflectivelyFindValIO(self: BaseModule): Option[Record] = { // Java reflection is faster and works for the common case def tryJavaReflect: Option[Record] = Try { self.getClass.getMethod("io").invoke(self).asInstanceOf[Record] }.toOption + .filter(_ != null) // Anonymous subclasses don't work with Java reflection, so try slower, Scala reflection def tryScalaReflect: Option[Record] = { val ru = scala.reflect.runtime.universe @@ -199,6 +200,7 @@ package object internal { Try { im.reflectField(term).get.asInstanceOf[Record] }.toOption + .filter(_ != null) } } @@ -209,13 +211,6 @@ package object internal { // Fallback if reflection fails, user can wrap in IO(...) self.findPort("io").collect { case r: Record => r } } - .getOrElse( - throwException( - s"Compatibility mode Module '$this' must have a 'val io' Bundle. " + - "If there is such a field and you still see this error, autowrapping has failed (sorry!). " + - "Please wrap the Bundle declaration in IO(...)." - ) - ) } /** Legacy Module class that restricts IOs to just io, clock, and reset, and provides a constructor @@ -243,17 +238,29 @@ package object internal { def this(_clock: Clock, _reset: Bool)(implicit moduleCompileOptions: CompileOptions) = this(Option(_clock), Option(_reset))(moduleCompileOptions) - private lazy val _io: Record = reflectivelyFindValIO(this) + // Sort of a DIY lazy val because if the user tries to construct hardware before val io is + // constructed, _compatAutoWrapPorts will try to access it but it will be null + // In that case, we basically need to delay setting this var until later + private var _ioValue: Option[Record] = None + private def _io: Option[Record] = _ioValue.orElse { + _ioValue = reflectivelyFindValIO(this) + _ioValue + } // Allow access to bindings from the compatibility package - protected def _compatIoPortBound() = portsContains(_io) + protected def _compatIoPortBound() = _io.exists(portsContains(_)) private[chisel3] override def generateComponent(): Option[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(...)") + if (_io.isEmpty || !_compatIoPortBound) { + throwException( + s"Compatibility mode Module '$this' must have a 'val io' Bundle. " + + "If there is such a field and you still see this error, autowrapping has failed (sorry!). " + + "Please wrap the Bundle declaration in IO(...)." + ) + } require( (portsContains(clock)) && (portsContains(reset)), "Internal error, module did not have clock or reset as IO" @@ -264,8 +271,8 @@ package object internal { } override def _compatAutoWrapPorts(): Unit = { - if (!_compatIoPortBound() && _io != null) { - _bindIoInPlace(_io) + if (!_compatIoPortBound()) { + _io.foreach(_bindIoInPlace(_)) } } } @@ -283,13 +290,13 @@ package object internal { implicit moduleCompileOptions: CompileOptions) extends chisel3.BlackBox(params) { - override private[chisel3] lazy val _io: Record = reflectivelyFindValIO(this) + override private[chisel3] lazy val _io: Option[Record] = reflectivelyFindValIO(this) // This class auto-wraps the BlackBox with IO(...), allowing legacy code (where IO(...) wasn't // required) to build. override def _compatAutoWrapPorts(): Unit = { if (!_compatIoPortBound()) { - _bindIoInPlace(_io) + _io.foreach(_bindIoInPlace(_)) } } } diff --git a/core/src/main/scala/chisel3/experimental/hierarchy/Definition.scala b/core/src/main/scala/chisel3/experimental/hierarchy/Definition.scala index b498daf0..59b4c692 100644 --- a/core/src/main/scala/chisel3/experimental/hierarchy/Definition.scala +++ b/core/src/main/scala/chisel3/experimental/hierarchy/Definition.scala @@ -100,7 +100,7 @@ object Definition extends SourceInfoDoc { implicit sourceInfo: SourceInfo, compileOptions: CompileOptions ): Definition[T] = { - val dynamicContext = new DynamicContext(Nil) + val dynamicContext = new DynamicContext(Nil, Builder.captureContext().throwOnFirstError) Builder.globalNamespace.copyTo(dynamicContext.globalNamespace) dynamicContext.inDefinition = true val (ir, module) = Builder.build(Module(proto), dynamicContext, false) diff --git a/core/src/main/scala/chisel3/internal/Builder.scala b/core/src/main/scala/chisel3/internal/Builder.scala index fb6ebcc7..247be57a 100644 --- a/core/src/main/scala/chisel3/internal/Builder.scala +++ b/core/src/main/scala/chisel3/internal/Builder.scala @@ -361,7 +361,7 @@ private[chisel3] class ChiselContext() { val viewNamespace = Namespace.empty } -private[chisel3] class DynamicContext(val annotationSeq: AnnotationSeq) { +private[chisel3] class DynamicContext(val annotationSeq: AnnotationSeq, val throwOnFirstError: Boolean) { val globalNamespace = Namespace.empty val components = ArrayBuffer[Component]() val annotations = ArrayBuffer[ChiselAnnotation]() @@ -504,7 +504,7 @@ private[chisel3] object Builder extends LazyLogging { def getPrefix: Prefix = chiselContext.get().prefixStack def currentModule: Option[BaseModule] = dynamicContextVar.value match { - case Some(dyanmicContext) => dynamicContext.currentModule + case Some(dynamicContext) => dynamicContext.currentModule case _ => None } def currentModule_=(target: Option[BaseModule]): Unit = { @@ -653,7 +653,8 @@ private[chisel3] object Builder extends LazyLogging { def errors: ErrorLog = dynamicContext.errors def error(m: => String): Unit = { - if (dynamicContextVar.value.isDefined) { + // If --throw-on-first-error is requested, throw an exception instead of aggregating errors + if (dynamicContextVar.value.isDefined && !dynamicContextVar.value.get.throwOnFirstError) { errors.error(m) } else { throwException(m) diff --git a/core/src/main/scala/chisel3/internal/Error.scala b/core/src/main/scala/chisel3/internal/Error.scala index c42f39ed..62086870 100644 --- a/core/src/main/scala/chisel3/internal/Error.scala +++ b/core/src/main/scala/chisel3/internal/Error.scala @@ -4,6 +4,7 @@ package chisel3.internal import scala.annotation.tailrec import scala.collection.mutable.{ArrayBuffer, LinkedHashMap} +import scala.util.control.NoStackTrace import _root_.logger.Logger object ExceptionHelpers { @@ -46,31 +47,36 @@ object ExceptionHelpers { } // Step 1: Remove elements from the top in the package trimlist - ((a: Array[StackTraceElement]) => a.dropWhile(inTrimlist)) - // Step 2: Optionally remove elements from the bottom until the anchor - .andThen(_.reverse) - .andThen(a => - anchor match { - case Some(b) => a.dropWhile(ste => !ste.getClassName.startsWith(b)) - case None => a - } - ) - // Step 3: Remove elements from the bottom in the package trimlist - .andThen(_.dropWhile(inTrimlist)) - // Step 4: Reverse back to the original order - .andThen(_.reverse.toArray) - // Step 5: Add ellipsis stack trace elements and "--full-stacktrace" info - .andThen(a => - ellipsis() +: - a :+ - ellipsis() :+ - ellipsis( - Some("Stack trace trimmed to user code only. Rerun with --full-stacktrace to see the full stack trace") - ) - ) - // Step 5: Mutate the stack trace in this exception - .andThen(throwable.setStackTrace(_)) - .apply(throwable.getStackTrace) + val trimStackTrace = + ((a: Array[StackTraceElement]) => a.dropWhile(inTrimlist)) + // Step 2: Optionally remove elements from the bottom until the anchor + .andThen(_.reverse) + .andThen(a => + anchor match { + case Some(b) => a.dropWhile(ste => !ste.getClassName.startsWith(b)) + case None => a + } + ) + // Step 3: Remove elements from the bottom in the package trimlist + .andThen(_.dropWhile(inTrimlist)) + // Step 4: Reverse back to the original order + .andThen(_.reverse.toArray) + // Step 5: Add ellipsis stack trace elements and "--full-stacktrace" info + .andThen(a => + ellipsis() +: + a :+ + ellipsis() :+ + ellipsis( + Some("Stack trace trimmed to user code only. Rerun with --full-stacktrace to see the full stack trace") + ) + ) + // Step 5: Mutate the stack trace in this exception + .andThen(throwable.setStackTrace(_)) + + val stackTrace = throwable.getStackTrace + if (stackTrace.nonEmpty) { + trimStackTrace(stackTrace) + } } } @@ -80,11 +86,11 @@ object ExceptionHelpers { class ChiselException(message: String, cause: Throwable = null) extends Exception(message, cause, true, true) { /** Package names whose stack trace elements should be trimmed when generating a trimmed stack trace */ - @deprecated("Use ExceptionHelpers.packageTrimlist. This will be removed in Chisel 3.6", "3.5") + @deprecated("Use ExceptionHelpers.packageTrimlist. This will be removed in Chisel 3.6", "Chisel 3.5") 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. */ - @deprecated("Use ExceptionHelpers.builderName. This will be removed in Chisel 3.6", "3.5") + @deprecated("Use ExceptionHelpers.builderName. This will be removed in Chisel 3.6", "Chisel 3.5") val builderName: String = chisel3.internal.Builder.getClass.getName /** Examine a [[Throwable]], to extract all its causes. Innermost cause is first. @@ -138,6 +144,11 @@ class ChiselException(message: String, cause: Throwable = null) extends Exceptio trimmedReverse.toIndexedSeq.reverse.toArray } + @deprecated( + "Use extension method trimStackTraceToUserCode defined in ExceptionHelpers.ThrowableHelpers. " + + "This will be removed in Chisel 3.6", + "Chisel 3.5.0" + ) def chiselStackTrace: String = { val trimmed = trimmedStackTrace(likelyCause) @@ -151,6 +162,7 @@ class ChiselException(message: String, cause: Throwable = null) extends Exceptio sw.toString } } +private[chisel3] class Errors(message: String) extends ChiselException(message) with NoStackTrace private[chisel3] object throwException { def apply(s: String, t: Throwable = null): Nothing = @@ -234,8 +246,10 @@ private[chisel3] class ErrorLog { } if (!allErrors.isEmpty) { - throw new ChiselException("Fatal errors during hardware elaboration. Look above for error list.") - with scala.util.control.NoStackTrace + throw new Errors( + "Fatal errors during hardware elaboration. Look above for error list. " + + "Rerun with --throw-on-first-error if you wish to see a stack trace." + ) } else { // No fatal errors, clear accumulated warnings since they've been reported errors.clear() |
