summaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/src/main/scala/chisel3/Aggregate.scala61
-rw-r--r--core/src/main/scala/chisel3/BlackBox.scala21
-rw-r--r--core/src/main/scala/chisel3/Data.scala2
-rw-r--r--core/src/main/scala/chisel3/Mem.scala164
-rw-r--r--core/src/main/scala/chisel3/RawModule.scala39
-rw-r--r--core/src/main/scala/chisel3/experimental/hierarchy/Definition.scala2
-rw-r--r--core/src/main/scala/chisel3/internal/Builder.scala7
-rw-r--r--core/src/main/scala/chisel3/internal/Error.scala72
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()