diff options
| author | Jim Lawson | 2016-10-06 11:15:08 -0700 |
|---|---|---|
| committer | Jim Lawson | 2016-10-06 11:15:08 -0700 |
| commit | d9e46d06522102634b04a187d5e89fe84b94678a (patch) | |
| tree | 3f44fc56acc334c1ffa7340583b29ad44ff8740b /chiselFrontend | |
| parent | f98171296f821034cf66ace070bcf179183e833d (diff) | |
| parent | 7aea39d4deac62d5477904f4bf4381c3482c41d0 (diff) | |
Merge branch 'master' into buildinfo
Diffstat (limited to 'chiselFrontend')
9 files changed, 138 insertions, 38 deletions
diff --git a/chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala b/chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala index 5cec54c2..9d8a9061 100644 --- a/chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala +++ b/chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala @@ -9,7 +9,7 @@ import scala.language.experimental.macros import chisel3.internal._ import chisel3.internal.Builder.pushCommand import chisel3.internal.firrtl._ -import chisel3.internal.sourceinfo.{SourceInfo, DeprecatedSourceInfo, VecTransform, SourceInfoTransform} +import chisel3.internal.sourceinfo.{SourceInfo, DeprecatedSourceInfo, VecTransform, SourceInfoTransform, UnlocatableSourceInfo} /** An abstract class for data types that solely consist of (are an aggregate * of) other Data objects. @@ -17,6 +17,8 @@ import chisel3.internal.sourceinfo.{SourceInfo, DeprecatedSourceInfo, VecTransfo sealed abstract class Aggregate extends Data { private[core] def cloneTypeWidth(width: Width): this.type = cloneType private[core] def width: Width = flatten.map(_.width).reduce(_ + _) + private[core] def legacyConnect(that: Data)(implicit sourceInfo: SourceInfo): Unit = + pushCommand(BulkConnect(sourceInfo, this.lref, that.lref)) } object Vec { @@ -113,6 +115,15 @@ object Vec { def do_fill[T <: Data](n: Int)(gen: => T)(implicit sourceInfo: SourceInfo): Vec[T] = apply(Seq.fill(n)(gen)) + + /** Truncate an index to implement modulo-power-of-2 addressing. */ + private[core] def truncateIndex(idx: UInt, n: Int)(implicit sourceInfo: SourceInfo): UInt = { + val w = BigInt(n-1).bitLength + if (n <= 1) UInt(0) + else if (idx.width.known && idx.width.get <= w) idx + else if (idx.width.known) idx(w-1,0) + else Wire(UInt(width = w), init = idx) + } } /** A vector (array) of [[Data]] elements. Provides hardware versions of various @@ -178,7 +189,8 @@ sealed class Vec[T <: Data] private (gen: T, val length: Int) def apply(idx: UInt): T = { Binding.checkSynthesizable(idx ,s"'idx' ($idx)") val port = sample_element.chiselCloneType - port.setRef(this, idx) //TODO(twigg): This is a bit too magical + val i = Vec.truncateIndex(idx, length)(UnlocatableSourceInfo) + port.setRef(this, i) // Bind each element of port to being whatever the base type is // Using the head element as the sample_element diff --git a/chiselFrontend/src/main/scala/chisel3/core/Assert.scala b/chiselFrontend/src/main/scala/chisel3/core/Assert.scala index db62f4a8..4782a845 100644 --- a/chiselFrontend/src/main/scala/chisel3/core/Assert.scala +++ b/chiselFrontend/src/main/scala/chisel3/core/Assert.scala @@ -24,21 +24,22 @@ object assert { // scalastyle:ignore object.name * * @param cond condition, assertion fires (simulation fails) when false * @param message optional message to print when the assertion fires + * @param data optional bits to print in the message formatting * * @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)(implicit sourceInfo: SourceInfo): Unit = macro apply_impl_msg + def apply(cond: Bool, message: String, data: Bits*)(implicit sourceInfo: SourceInfo): Unit = macro apply_impl_msg_data def apply(cond: Bool)(implicit sourceInfo: SourceInfo): Unit = macro apply_impl - def apply_impl_msg(c: Context)(cond: c.Tree, message: c.Tree)(sourceInfo: c.Tree): c.Tree = { + def apply_impl_msg_data(c: Context)(cond: c.Tree, message: c.Tree, data: c.Tree*)(sourceInfo: 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.Some($message))($sourceInfo)" + q"$apply_impl_do($cond, $condStr, _root_.scala.Some($message), ..$data)($sourceInfo)" } def apply_impl(c: Context)(cond: c.Tree)(sourceInfo: c.Tree): c.Tree = { @@ -49,11 +50,11 @@ object assert { // scalastyle:ignore object.name q"$apply_impl_do($cond, $condStr, _root_.scala.None)($sourceInfo)" } - def apply_impl_do(cond: Bool, line: String, message: Option[String])(implicit sourceInfo: SourceInfo) { + def apply_impl_do(cond: Bool, line: String, message: Option[String], data: Bits*)(implicit sourceInfo: SourceInfo) { when (!(cond || Builder.forcedModule.reset)) { message match { - case Some(str) => printf.printfWithoutReset(s"Assertion failed: $str\n at $line\n") - case None => printf.printfWithoutReset(s"Assertion failed\n at $line\n") + case Some(str) => printf.printfWithoutReset(s"Assertion failed: $str\n at $line\n", data:_*) + case None => printf.printfWithoutReset(s"Assertion failed\n at $line\n", data:_*) } pushCommand(Stop(sourceInfo, Node(Builder.forcedModule.clock), 1)) } diff --git a/chiselFrontend/src/main/scala/chisel3/core/Binding.scala b/chiselFrontend/src/main/scala/chisel3/core/Binding.scala index 5378f3ae..467cb4eb 100644 --- a/chiselFrontend/src/main/scala/chisel3/core/Binding.scala +++ b/chiselFrontend/src/main/scala/chisel3/core/Binding.scala @@ -53,6 +53,7 @@ object Binding { case class BindingException(message: String) extends Exception(message) def AlreadyBoundException(binding: String) = BindingException(s": Already bound to $binding") def NotSynthesizableException = BindingException(s": Not bound to synthesizable node, currently only Type description") + def MissingIOWrapperException = BindingException(": Missing IO() wrapper") // This recursively walks down the Data tree to look at all the leaf 'Element's // Will build up an error string in case something goes wrong @@ -138,6 +139,29 @@ object Binding { } } } + + /** Diagnose a binding error caused by a missing IO() wrapper. + * @param element the element triggering the binding error. + * @return true if the element is a member of the module's io but ioDefined is false. + */ + def isMissingIOWrapper(element: Element): Boolean = { + element._parent match { + case None => false + case Some(x: Module) => { + // If the IO() wrapper has been executed, it isn't missing. + if (x.ioDefined) { + false + } else { + // TODO: We should issue the message only once, and if we get here, + // we know the wrapper is missing, whether or not the element is a member of io. + // But if it's not an io element, we want to issue the complementary "unbound" error. + // Revisit this when we collect error messages instead of throwing exceptions. + x.io.flatten.contains(element) + } + } + } + } + try walkToBinding( target, element => element.binding match { @@ -145,10 +169,16 @@ object Binding { case binding => // The following kludge is an attempt to provide backward compatibility // It should be done at at higher level. - if ((forcedModule.compileOptions.requireIOWrap || !elementOfIO(element))) - throw NotSynthesizableException - else + if ((forcedModule.compileOptions.requireIOWrap || !elementOfIO(element))) { + // Generate a better error message if this is a result of a missing IO() wrapper. + if (isMissingIOWrapper(element)) { + throw MissingIOWrapperException + } else { + throw NotSynthesizableException + } + } else { Binding.bind(element, PortBinder(element._parent.get), "Error: IO") + } } ) catch { diff --git a/chiselFrontend/src/main/scala/chisel3/core/Bits.scala b/chiselFrontend/src/main/scala/chisel3/core/Bits.scala index 24abbdba..741f6aee 100644 --- a/chiselFrontend/src/main/scala/chisel3/core/Bits.scala +++ b/chiselFrontend/src/main/scala/chisel3/core/Bits.scala @@ -5,7 +5,7 @@ package chisel3.core import scala.language.experimental.macros import chisel3.internal._ -import chisel3.internal.Builder.pushOp +import chisel3.internal.Builder.{pushCommand, pushOp} import chisel3.internal.firrtl._ import chisel3.internal.sourceinfo.{SourceInfo, DeprecatedSourceInfo, SourceInfoTransform, SourceInfoWhiteboxTransform, UIntTransform, MuxTransform} @@ -40,6 +40,9 @@ abstract class Element(private[core] val width: Width) extends Data { private[chisel3] final def allElements: Seq[Element] = Seq(this) def widthKnown: Boolean = width.known def name: String = getRef.name + + private[core] def legacyConnect(that: Data)(implicit sourceInfo: SourceInfo): Unit = + pushCommand(Connect(sourceInfo, this.lref, that.ref)) } /** A data type for values represented by a single bitvector. Provides basic diff --git a/chiselFrontend/src/main/scala/chisel3/core/CompileOptions.scala b/chiselFrontend/src/main/scala/chisel3/core/CompileOptions.scala index 0e66a241..4dea39b5 100644 --- a/chiselFrontend/src/main/scala/chisel3/core/CompileOptions.scala +++ b/chiselFrontend/src/main/scala/chisel3/core/CompileOptions.scala @@ -17,6 +17,11 @@ trait CompileOptions { val dontTryConnectionsSwapped: Boolean // If connection directionality is not explicit, do not use heuristics to attempt to determine it. val dontAssumeDirectionality: Boolean + // Issue a deprecation warning if Data.{flip, asInput,asOutput} is used + // instead of Flipped, Input, or Output. + val deprecateOldDirectionMethods: Boolean + // Check that referenced Data have actually been declared. + val checkSynthesizable: Boolean } object CompileOptions { @@ -34,6 +39,8 @@ object ExplicitCompileOptions { val requireIOWrap = false val dontTryConnectionsSwapped = false val dontAssumeDirectionality = false + val deprecateOldDirectionMethods = false + val checkSynthesizable = false } // Collection of "strict" connection compile options, preferred for new code. @@ -44,5 +51,7 @@ object ExplicitCompileOptions { val requireIOWrap = true val dontTryConnectionsSwapped = true val dontAssumeDirectionality = true + val deprecateOldDirectionMethods = true + val checkSynthesizable = true } } diff --git a/chiselFrontend/src/main/scala/chisel3/core/Data.scala b/chiselFrontend/src/main/scala/chisel3/core/Data.scala index 3f21a34c..bd2e9065 100644 --- a/chiselFrontend/src/main/scala/chisel3/core/Data.scala +++ b/chiselFrontend/src/main/scala/chisel3/core/Data.scala @@ -104,12 +104,21 @@ object Data { } implicit class AddDirectionToData[T<:Data](val target: T) extends AnyVal { - @deprecated("Input(Data) should be used over Data.asInput", "gchisel") - def asInput: T = Input(target) - @deprecated("Output(Data) should be used over Data.asOutput", "gchisel") - def asOutput: T = Output(target) - @deprecated("Flipped(Data) should be used over Data.flip", "gchisel") - def flip(): T = Flipped(target) + def asInput(implicit opts: CompileOptions): T = { + if (opts.deprecateOldDirectionMethods) + Builder.deprecated("Input(Data) should be used over Data.asInput") + Input(target) + } + def asOutput(implicit opts: CompileOptions): T = { + if (opts.deprecateOldDirectionMethods) + Builder.deprecated("Output(Data) should be used over Data.asOutput") + Output(target) + } + def flip()(implicit opts: CompileOptions): T = { + if (opts.deprecateOldDirectionMethods) + Builder.deprecated("Flipped(Data) should be used over Data.flip") + Flipped(target) + } } } @@ -126,27 +135,35 @@ abstract class Data extends HasId { private[core] 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 = { - Binding.checkSynthesizable(this, s"'this' ($this)") - Binding.checkSynthesizable(that, s"'that' ($that)") - try { - MonoConnect.connect(sourceInfo, connectCompileOptions, this, that, Builder.forcedModule) - } catch { - case MonoConnect.MonoConnectException(message) => - throwException( - s"Connection between sink ($this) and source ($that) failed @$message" - ) + if (connectCompileOptions.checkSynthesizable) { + Binding.checkSynthesizable(this, s"'this' ($this)") + Binding.checkSynthesizable(that, s"'that' ($that)") + try { + MonoConnect.connect(sourceInfo, connectCompileOptions, this, that, Builder.forcedModule) + } catch { + case MonoConnect.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 = { - Binding.checkSynthesizable(this, s"'this' ($this)") - Binding.checkSynthesizable(that, s"'that' ($that)") - try { - BiConnect.connect(sourceInfo, connectCompileOptions, this, that, Builder.forcedModule) - } catch { - case BiConnect.BiConnectException(message) => - throwException( - s"Connection between left ($this) and source ($that) failed @$message" - ) + if (connectCompileOptions.checkSynthesizable) { + Binding.checkSynthesizable(this, s"'this' ($this)") + Binding.checkSynthesizable(that, s"'that' ($that)") + try { + BiConnect.connect(sourceInfo, connectCompileOptions, this, that, Builder.forcedModule) + } catch { + case BiConnect.BiConnectException(message) => + throwException( + s"Connection between left ($this) and source ($that) failed @$message" + ) + } + } else { + this legacyConnect that } } private[chisel3] def lref: Node = Node(this) @@ -154,8 +171,21 @@ abstract class Data extends HasId { private[core] def cloneTypeWidth(width: Width): this.type private[chisel3] def toType: String private[core] def width: Width + private[core] def legacyConnect(that: Data)(implicit sourceInfo: SourceInfo): Unit + /** cloneType must be defined for any Chisel object extending Data. + * It is responsible for constructing a basic copy of the object being cloned. + * If cloneType needs to recursively clone elements of an object, it should call + * the cloneType methods on those elements. + * @return a copy of the object. + */ def cloneType: this.type + + /** chiselCloneType is called at the top-level of a clone chain. + * It calls the client's cloneType() method to construct a basic copy of the object being cloned, + * then performs any fixups required to reconstruct the appropriate core state of the cloned object. + * @return a copy of the object with appropriate core state. + */ def chiselCloneType: this.type = { // Call the user-supplied cloneType method val clone = this.cloneType @@ -163,6 +193,7 @@ abstract class Data extends HasId { //TODO(twigg): Do recursively for better error messages for((clone_elem, source_elem) <- clone.allElements zip this.allElements) { clone_elem.binding = UnboundBinding(source_elem.binding.direction) + Data.setFirrtlDirection(clone_elem, Data.getFirrtlDirection(source_elem)) } clone } diff --git a/chiselFrontend/src/main/scala/chisel3/core/Mem.scala b/chiselFrontend/src/main/scala/chisel3/core/Mem.scala index b24d463a..9cd5a4d8 100644 --- a/chiselFrontend/src/main/scala/chisel3/core/Mem.scala +++ b/chiselFrontend/src/main/scala/chisel3/core/Mem.scala @@ -38,7 +38,10 @@ sealed abstract class MemBase[T <: Data](t: T, val length: Int) extends HasId wi /** Creates a read accessor into the memory with static addressing. See the * class documentation of the memory for more detailed information. */ - def apply(idx: Int): T = apply(UInt(idx)) + def apply(idx: Int): T = { + require(idx >= 0 && idx < length) + apply(UInt(idx)) + } /** Creates a read/write accessor into the memory with dynamic addressing. * See the class documentation of the memory for more detailed information. @@ -85,10 +88,11 @@ sealed abstract class MemBase[T <: Data](t: T, val length: Int) extends HasId wi private def makePort(sourceInfo: SourceInfo, idx: UInt, dir: MemPortDirection): T = { Binding.checkSynthesizable(idx, s"'idx' ($idx)") + val i = Vec.truncateIndex(idx, length)(sourceInfo) val port = pushCommand( DefMemPort(sourceInfo, - t.chiselCloneType, Node(this), dir, idx.ref, Node(idx._parent.get.clock)) + t.chiselCloneType, Node(this), dir, i.ref, Node(i._parent.get.clock)) ).id // Bind each element of port to being a MemoryPort Binding.bind(port, MemoryPortBinder(Builder.forcedModule), "Error: Fresh t") diff --git a/chiselFrontend/src/main/scala/chisel3/internal/Builder.scala b/chiselFrontend/src/main/scala/chisel3/internal/Builder.scala index 8d376810..12cc840e 100644 --- a/chiselFrontend/src/main/scala/chisel3/internal/Builder.scala +++ b/chiselFrontend/src/main/scala/chisel3/internal/Builder.scala @@ -173,6 +173,8 @@ private[chisel3] object Builder { def errors: ErrorLog = dynamicContext.errors def error(m: => String): Unit = errors.error(m) + def warning(m: => String): Unit = errors.warning(m) + def deprecated(m: => String): Unit = errors.deprecated(m) def build[T <: Module](f: => T): Circuit = { dynamicContextVar.withValue(Some(new DynamicContext())) { diff --git a/chiselFrontend/src/main/scala/chisel3/internal/Error.scala b/chiselFrontend/src/main/scala/chisel3/internal/Error.scala index 7ae0580f..c5c67da4 100644 --- a/chiselFrontend/src/main/scala/chisel3/internal/Error.scala +++ b/chiselFrontend/src/main/scala/chisel3/internal/Error.scala @@ -25,6 +25,10 @@ private[chisel3] class ErrorLog { def warning(m: => String): Unit = errors += new Warning(m, getUserLineNumber) + /** Log a deprecation warning message */ + def deprecated(m: => String): Unit = + errors += new DeprecationWarning(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 @@ -86,6 +90,10 @@ private class Warning(msg: => String, line: Option[StackTraceElement]) extends L def format: String = tag("warn", Console.YELLOW) } +private class DeprecationWarning(msg: => String, line: Option[StackTraceElement]) extends LogEntry(msg, line) { + def format: String = tag("warn", Console.CYAN) +} + private class Info(msg: => String, line: Option[StackTraceElement]) extends LogEntry(msg, line) { def format: String = tag("info", Console.MAGENTA) } |
