summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Lin2019-01-22 15:06:08 -0800
committerGitHub2019-01-22 15:06:08 -0800
commitb39bc25ff76762d93300975e533ec3bd4f119d1c (patch)
tree38a8219d1c8ef6c61fd667a3f46a189091e2ea20
parent40f4d3edf93cf48294bd6555a1deaa07571b2d3d (diff)
Define Data .toString (#985)
toString on Data subtypes will now print the type and optionally binding information including literals and IO names as feasible.
-rw-r--r--chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala23
-rw-r--r--chiselFrontend/src/main/scala/chisel3/core/Bits.scala34
-rw-r--r--chiselFrontend/src/main/scala/chisel3/core/Clock.scala2
-rw-r--r--chiselFrontend/src/main/scala/chisel3/core/Data.scala35
-rw-r--r--chiselFrontend/src/main/scala/chisel3/core/StrongEnum.scala17
-rw-r--r--src/test/scala/chiselTests/DataPrint.scala92
-rw-r--r--src/test/scala/chiselTests/PrintableSpec.scala2
7 files changed, 202 insertions, 3 deletions
diff --git a/chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala b/chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala
index 5563092e..e85b7158 100644
--- a/chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala
+++ b/chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala
@@ -150,6 +150,10 @@ object Vec extends VecFactory
*/
sealed class Vec[T <: Data] private[core] (gen: => T, val length: Int)
extends Aggregate with VecLike[T] {
+ override def toString: String = {
+ s"$sample_element[$length]$bindingToString"
+ }
+
private[core] override def typeEquivalent(that: Data): Boolean = that match {
case that: Vec[T] =>
this.length == that.length &&
@@ -448,6 +452,18 @@ abstract class Record(private[chisel3] implicit val compileOptions: CompileOptio
* 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 */
@@ -543,8 +559,11 @@ class AutoClonetypeException(message: String) extends ChiselException(message)
* }}}
*/
abstract class Bundle(implicit compileOptions: CompileOptions) extends Record {
- override def className = "Bundle"
-
+ 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
diff --git a/chiselFrontend/src/main/scala/chisel3/core/Bits.scala b/chiselFrontend/src/main/scala/chisel3/core/Bits.scala
index 83c8637a..db2562a5 100644
--- a/chiselFrontend/src/main/scala/chisel3/core/Bits.scala
+++ b/chiselFrontend/src/main/scala/chisel3/core/Bits.scala
@@ -644,6 +644,13 @@ abstract trait Num[T <: Data] {
* @define constantWidth @note The width of the returned $coll is unchanged, i.e., `width of this`.
*/
sealed class UInt private[core] (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[core] override def typeEquivalent(that: Data): Boolean =
that.isInstanceOf[UInt] && this.width == that.width
@@ -959,6 +966,13 @@ object Bits extends UIntFactory
* @define constantWidth @note The width of the returned $coll is unchanged, i.e., `width of this`.
*/
sealed class SInt private[core] (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[core] override def typeEquivalent(that: Data): Boolean =
this.getClass == that.getClass && this.width == that.width // TODO: should this be true for unspecified widths?
@@ -1207,6 +1221,14 @@ sealed trait Reset extends Element with ToBoolable
* @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[core] override def cloneTypeWidth(w: Width): this.type = {
require(!w.known || w.get == 1)
new Bool().asInstanceOf[this.type]
@@ -1328,6 +1350,14 @@ object Bool extends BoolFactory
*/
sealed class FixedPoint private (width: Width, val binaryPoint: BinaryPoint)
extends Bits(width) with Num[FixedPoint] {
+ override def toString: String = {
+ val bindingString = litToDoubleOption match {
+ case Some(value) => s"($value)"
+ case _ => bindingToString
+ }
+ s"FixedPoint$width$binaryPoint$bindingString"
+ }
+
private[core] 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
@@ -1724,6 +1754,10 @@ object FixedPoint {
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[core] override def typeEquivalent(that: Data): Boolean =
that.isInstanceOf[Analog] && this.width == that.width
diff --git a/chiselFrontend/src/main/scala/chisel3/core/Clock.scala b/chiselFrontend/src/main/scala/chisel3/core/Clock.scala
index 88208d9a..1bc2260c 100644
--- a/chiselFrontend/src/main/scala/chisel3/core/Clock.scala
+++ b/chiselFrontend/src/main/scala/chisel3/core/Clock.scala
@@ -13,6 +13,8 @@ object 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[core] def typeEquivalent(that: Data): Boolean =
diff --git a/chiselFrontend/src/main/scala/chisel3/core/Data.scala b/chiselFrontend/src/main/scala/chisel3/core/Data.scala
index cf6dabdd..bffd84df 100644
--- a/chiselFrontend/src/main/scala/chisel3/core/Data.scala
+++ b/chiselFrontend/src/main/scala/chisel3/core/Data.scala
@@ -87,10 +87,23 @@ object DataMirror {
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 {
def isSynthesizable(target: Data) = target.topBindingOpt.isDefined
@@ -292,6 +305,26 @@ abstract class Data extends HasId with NamedComponent with SourceInfoDoc {
_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.
+ 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 == 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
@@ -646,6 +679,8 @@ object DontCare extends Element {
bind(DontCareBinding(), SpecifiedDirection.Output)
override def cloneType = DontCare
+ override def toString: String = "DontCare()"
+
override def litOption = None
def toPrintable: Printable = PString("DONTCARE")
diff --git a/chiselFrontend/src/main/scala/chisel3/core/StrongEnum.scala b/chiselFrontend/src/main/scala/chisel3/core/StrongEnum.scala
index 889e9b11..8feb0541 100644
--- a/chiselFrontend/src/main/scala/chisel3/core/StrongEnum.scala
+++ b/chiselFrontend/src/main/scala/chisel3/core/StrongEnum.scala
@@ -33,6 +33,19 @@ import EnumAnnotations._
abstract class EnumType(private val factory: EnumFactory, selfAnnotating: Boolean = false) 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[core] def compop(sourceInfo: SourceInfo, op: PrimOp, other: EnumType): Bool = {
@@ -151,6 +164,10 @@ abstract class EnumFactory {
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
protected def Value(id: UInt): Type = macro EnumMacros.ValCustomImpl
diff --git a/src/test/scala/chiselTests/DataPrint.scala b/src/test/scala/chiselTests/DataPrint.scala
new file mode 100644
index 00000000..662d8e1a
--- /dev/null
+++ b/src/test/scala/chiselTests/DataPrint.scala
@@ -0,0 +1,92 @@
+// See LICENSE for license details.
+
+package chiselTests
+
+import org.scalatest._
+
+import chisel3._
+import chisel3.experimental.{ChiselEnum, FixedPoint, RawModule, MultiIOModule}
+
+class DataPrintSpec extends ChiselFlatSpec with Matchers {
+ object EnumTest extends ChiselEnum {
+ val sNone, sOne, sTwo = Value
+ }
+
+ // TODO: dedup w/ BundleLiteralSpec
+ class BundleTest extends Bundle {
+ val a = UInt(8.W)
+ val b = Bool()
+
+ // Bundle literal constructor code, which will be auto-generated using macro annotations in
+ // the future.
+ import chisel3.core.BundleLitBinding
+ import chisel3.internal.firrtl.{ULit, Width}
+ // Full bundle literal constructor
+ def Lit(aVal: UInt, bVal: Bool): BundleTest = {
+ val clone = cloneType
+ clone.selfBind(BundleLitBinding(Map(
+ clone.a -> litArgOfBits(aVal),
+ clone.b -> litArgOfBits(bVal)
+ )))
+ clone
+ }
+ }
+
+ "Data types" should "have a meaningful string representation" in {
+ elaborate { new RawModule {
+ UInt().toString should be ("UInt")
+ UInt(8.W).toString should be ("UInt<8>")
+ SInt(15.W).toString should be ("SInt<15>")
+ Bool().toString should be ("Bool")
+ Clock().toString should be ("Clock")
+ FixedPoint(5.W, 3.BP).toString should be ("FixedPoint<5><<3>>")
+ Vec(3, UInt(2.W)).toString should be ("UInt<2>[3]")
+ EnumTest.Type().toString should be ("EnumTest")
+ (new BundleTest).toString should be ("BundleTest")
+ } }
+ }
+
+ class BoundDataModule extends MultiIOModule { // not in the test to avoid anon naming suffixes
+ Wire(UInt()).toString should be("UInt(Wire in DataPrintSpec$BoundDataModule)")
+ Reg(SInt()).toString should be("SInt(Reg in DataPrintSpec$BoundDataModule)")
+ val io = IO(Output(Bool())) // needs a name so elaboration doesn't fail
+ io.toString should be("Bool(IO in unelaborated DataPrintSpec$BoundDataModule)")
+ val m = Mem(4, UInt(2.W))
+ m(2).toString should be("UInt<2>(MemPort in DataPrintSpec$BoundDataModule)")
+ (2.U + 2.U).toString should be("UInt<2>(OpResult in DataPrintSpec$BoundDataModule)")
+ Wire(Vec(3, UInt(2.W))).toString should be ("UInt<2>[3](Wire in DataPrintSpec$BoundDataModule)")
+
+ class InnerModule extends MultiIOModule {
+ val io = IO(Output(new Bundle {
+ val a = UInt(4.W)
+ }))
+ }
+ val inner = Module(new InnerModule)
+ inner.clock.toString should be ("Clock(IO clock in DataPrintSpec$BoundDataModule$InnerModule)")
+ inner.io.a.toString should be ("UInt<4>(IO io_a in DataPrintSpec$BoundDataModule$InnerModule)")
+ }
+
+ "Bound data types" should "have a meaningful string representation" in {
+ elaborate { new BoundDataModule }
+ }
+
+ "Literals" should "have a meaningful string representation" in {
+ elaborate { new RawModule {
+ 3.U.toString should be ("UInt<2>(3)")
+ 3.U(5.W).toString should be ("UInt<5>(3)")
+ -1.S.toString should be ("SInt<1>(-1)")
+ false.B.toString should be ("Bool(false)")
+ true.B.toString should be ("Bool(true)")
+ 2.25.F(6.W, 2.BP).toString should be ("FixedPoint<6><<2>>(2.25)")
+ -2.25.F(6.W, 2.BP).toString should be ("FixedPoint<6><<2>>(-2.25)")
+ EnumTest.sNone.toString should be ("EnumTest(0=sNone)")
+ EnumTest.sTwo.toString should be ("EnumTest(2=sTwo)")
+ EnumTest(1.U).toString should be ("EnumTest(1=sOne)")
+ (new BundleTest).Lit(2.U, false.B).toString should be ("BundleTest(a=UInt<8>(2), b=Bool(false))")
+ new Bundle {
+ val a = UInt(8.W)
+ }.toString should be ("AnonymousBundle")
+ DontCare.toString should be ("DontCare()")
+ } }
+ }
+}
diff --git a/src/test/scala/chiselTests/PrintableSpec.scala b/src/test/scala/chiselTests/PrintableSpec.scala
index c6ba0a1d..6d74fbc0 100644
--- a/src/test/scala/chiselTests/PrintableSpec.scala
+++ b/src/test/scala/chiselTests/PrintableSpec.scala
@@ -177,7 +177,7 @@ class PrintableSpec extends FlatSpec with Matchers {
}
val firrtl = Driver.emit(() => new MyModule)
getPrintfs(firrtl) match {
- case Seq(Printf("Bundle(foo -> %d, bar -> %d)",
+ case Seq(Printf("AnonymousBundle(foo -> %d, bar -> %d)",
Seq("myBun.foo", "myBun.bar"))) =>
case e => fail()
}