summaryrefslogtreecommitdiff
path: root/chiselFrontend/src
diff options
context:
space:
mode:
authorJack Koenig2016-09-07 14:18:29 -0700
committerGitHub2016-09-07 14:18:29 -0700
commit0ed5eb48cdb916b644aaf9e5dbf48f6cfb6c60f4 (patch)
tree71829e27aa3c57e682c8231c27027b3efbd2918d /chiselFrontend/src
parent16426b3a68d85ce7dd9655b0ce773431eb69fc74 (diff)
Add Printable (#270)
Printable is a new type that changes how printing of Chisel types is represented It uses an ordered collection rather than a format string and specifiers Features: - Custom String Interpolator for Scala-like printf - String-like manipulation of "hardware strings" for custom pretty-printing - Default pretty-printing for Chisel data types
Diffstat (limited to 'chiselFrontend/src')
-rw-r--r--chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala26
-rw-r--r--chiselFrontend/src/main/scala/chisel3/core/Bits.scala3
-rw-r--r--chiselFrontend/src/main/scala/chisel3/core/Data.scala6
-rw-r--r--chiselFrontend/src/main/scala/chisel3/core/Printable.scala150
-rw-r--r--chiselFrontend/src/main/scala/chisel3/core/Printf.scala44
-rw-r--r--chiselFrontend/src/main/scala/chisel3/internal/firrtl/IR.scala18
6 files changed, 225 insertions, 22 deletions
diff --git a/chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala b/chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala
index f80c8d8e..d07c5dcd 100644
--- a/chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala
+++ b/chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala
@@ -180,6 +180,17 @@ sealed class Vec[T <: Data] private (gen: => T, val length: Int)
for ((elt, i) <- self zipWithIndex)
elt.setRef(this, i)
+
+ /** Default "pretty-print" implementation
+ * Analogous to printing a Seq
+ * Results in "Vec(elt0, elt1, ...)"
+ */
+ def toPrintable: Printable = {
+ val elts =
+ if (length == 0) List.empty[Printable]
+ else self flatMap (e => List(e.toPrintable, PString(", "))) dropRight 1
+ PString("Vec(") + Printables(elts) + PString(")")
+ }
}
/** A trait for [[Vec]]s containing common hardware generators for collection
@@ -381,6 +392,21 @@ class Bundle extends Aggregate(NO_DIR) {
this
}
}
+
+ /** Default "pretty-print" implementation
+ * Analogous to printing a Map
+ * Results in "Bundle(elt0.name -> elt0.value, ...)"
+ */
+ def toPrintable: Printable = {
+ val elts =
+ if (elements.isEmpty) List.empty[Printable]
+ else {
+ elements.toList.reverse flatMap { case (name, data) =>
+ List(PString(s"$name -> "), data.toPrintable, PString(", "))
+ } dropRight 1 // Remove trailing ", "
+ }
+ PString("Bundle(") + Printables(elts) + PString(")")
+ }
}
private[core] object Bundle {
diff --git a/chiselFrontend/src/main/scala/chisel3/core/Bits.scala b/chiselFrontend/src/main/scala/chisel3/core/Bits.scala
index 57f6d8e0..77a31252 100644
--- a/chiselFrontend/src/main/scala/chisel3/core/Bits.scala
+++ b/chiselFrontend/src/main/scala/chisel3/core/Bits.scala
@@ -250,6 +250,9 @@ sealed abstract class Bits(dirArg: Direction, width: Width, override val litArg:
res := that
res
}
+
+ /** Default print as [[Decimal]] */
+ final def toPrintable: Printable = Decimal(this)
}
/** Provides a set of operations to create UInt types and literals.
diff --git a/chiselFrontend/src/main/scala/chisel3/core/Data.scala b/chiselFrontend/src/main/scala/chisel3/core/Data.scala
index 5ae8c84d..573632ab 100644
--- a/chiselFrontend/src/main/scala/chisel3/core/Data.scala
+++ b/chiselFrontend/src/main/scala/chisel3/core/Data.scala
@@ -132,6 +132,9 @@ abstract class Data(dirArg: Direction) extends HasId {
def do_asUInt(implicit sourceInfo: SourceInfo): UInt =
SeqUtils.do_asUInt(this.flatten)(sourceInfo)
+
+ /** Default pretty printing */
+ def toPrintable: Printable
}
object Wire {
@@ -171,4 +174,7 @@ sealed class Clock(dirArg: Direction) extends Element(dirArg, Width(1)) {
case _: Clock => this connect that
case _ => this badConnect that
}
+
+ /** Not really supported */
+ def toPrintable: Printable = PString("CLOCK")
}
diff --git a/chiselFrontend/src/main/scala/chisel3/core/Printable.scala b/chiselFrontend/src/main/scala/chisel3/core/Printable.scala
new file mode 100644
index 00000000..13d5d74e
--- /dev/null
+++ b/chiselFrontend/src/main/scala/chisel3/core/Printable.scala
@@ -0,0 +1,150 @@
+// See LICENSE for license details.
+
+package chisel3.core
+
+import chisel3.internal.firrtl.Component
+import chisel3.internal.HasId
+
+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"..."
+ * 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: (String, Iterable[String])
+ /** Allow for appending Printables like Strings */
+ final def +(that: Printable) = Printables(List(this, that))
+ /** Allow for appending Strings to Printables */
+ final def +(that: String) = Printables(List(this, PString(that)))
+}
+object Printable {
+ /** Pack standard printf fmt, args* style into Printable
+ */
+ def pack(fmt: String, data: Data*): Printable = {
+ 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: (String, Iterable[String]) = {
+ val (fmts, args) = pables.map(_.unpack).unzip
+ (fmts.mkString, args.flatten)
+ }
+}
+/** Wrapper for printing Scala Strings */
+case class PString(str: String) extends Printable {
+ final def unpack: (String, Iterable[String]) =
+ (str replaceAll ("%", "%%"), List.empty)
+}
+/** Superclass for Firrtl format specifiers for Bits */
+sealed abstract class FirrtlFormat(specifier: Char) extends Printable {
+ def bits: Bits
+ def unpack: (String, Iterable[String]) = {
+ val id = if (bits.isLit) bits.ref.name else bits.instanceName
+ (s"%$specifier", List(id))
+ }
+}
+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: (String, Iterable[String]) = (data.ref.name, List.empty)
+}
+/** Put full name within parent namespace (eg. bundleName.field) */
+case class FullName(hasId: HasId) extends Printable {
+ final def unpack: (String, Iterable[String]) = (hasId.instanceName, List.empty)
+}
+/** Represents escaped percents */
+case object Percent extends Printable {
+ final def unpack: (String, Iterable[String]) = ("%%", List.empty)
+}
diff --git a/chiselFrontend/src/main/scala/chisel3/core/Printf.scala b/chiselFrontend/src/main/scala/chisel3/core/Printf.scala
index b0a3c955..55197425 100644
--- a/chiselFrontend/src/main/scala/chisel3/core/Printf.scala
+++ b/chiselFrontend/src/main/scala/chisel3/core/Printf.scala
@@ -10,6 +10,24 @@ import chisel3.internal.firrtl._
import chisel3.internal.sourceinfo.SourceInfo
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)
+ if (x == '"' || x == '\\') {
+ s"\\${x}"
+ } else if (x == '\n') {
+ "\\n"
+ } else {
+ require(x.toInt >= 32) // TODO \xNN once FIRRTL issue #59 is resolved
+ x
+ }
+ }
+ formatIn map escaped mkString ""
+ }
+
/** Prints a message in simulation.
*
* Does not fire when in reset (defined as the encapsulating Module's
@@ -23,14 +41,30 @@ object printf { // scalastyle:ignore object.name
* @param fmt printf format string
* @param data format string varargs containing data to print
*/
- def apply(fmt: String, data: Bits*)(implicit sourceInfo: SourceInfo) {
- when (!(Builder.dynamicContext.currentModule.get.reset)) {
- printfWithoutReset(fmt, data:_*)
+ def apply(fmt: String, data: Bits*)(implicit sourceInfo: SourceInfo): Unit =
+ apply(Printable.pack(fmt, data:_*))
+ /** Prints a message in simulation.
+ *
+ * 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 printf make the standard Module assumptions (single clock
+ * and single reset).
+ *
+ * @param pable [[Printable]] to print
+ */
+ def apply(pable: Printable)(implicit sourceInfo: SourceInfo): Unit = {
+ when (!Builder.dynamicContext.currentModule.get.reset) {
+ printfWithoutReset(pable)
}
}
- private[core] def printfWithoutReset(fmt: String, data: Bits*)(implicit sourceInfo: SourceInfo) {
+ private[chisel3] def printfWithoutReset(pable: Printable)(implicit sourceInfo: SourceInfo): Unit = {
val clock = Builder.dynamicContext.currentModule.get.clock
- pushCommand(Printf(sourceInfo, Node(clock), fmt, data.map((d: Bits) => d.ref)))
+ pushCommand(Printf(sourceInfo, Node(clock), pable))
}
+ private[chisel3] def printfWithoutReset(fmt: String, data: Bits*)(implicit sourceInfo: SourceInfo): Unit =
+ printfWithoutReset(Printable.pack(fmt, data:_*))
}
diff --git a/chiselFrontend/src/main/scala/chisel3/internal/firrtl/IR.scala b/chiselFrontend/src/main/scala/chisel3/internal/firrtl/IR.scala
index 64d7d5fd..1f05b6e2 100644
--- a/chiselFrontend/src/main/scala/chisel3/internal/firrtl/IR.scala
+++ b/chiselFrontend/src/main/scala/chisel3/internal/firrtl/IR.scala
@@ -169,22 +169,6 @@ case class ConnectInit(sourceInfo: SourceInfo, loc: Node, exp: Arg) extends Comm
case class Stop(sourceInfo: SourceInfo, clk: Arg, ret: Int) extends Command
case class Component(id: Module, name: String, ports: Seq[Port], commands: Seq[Command]) extends Arg
case class Port(id: Data, dir: Direction)
-case class Printf(sourceInfo: SourceInfo, clk: Arg, formatIn: String, ids: Seq[Arg]) extends Command {
- require(formatIn.forall(c => c.toInt > 0 && c.toInt < 128), "format strings must comprise non-null ASCII values")
- def format: String = {
- def escaped(x: Char) = {
- require(x.toInt >= 0)
- if (x == '"' || x == '\\') {
- s"\\${x}"
- } else if (x == '\n') {
- "\\n"
- } else {
- require(x.toInt >= 32) // TODO \xNN once FIRRTL issue #59 is resolved
- x
- }
- }
- formatIn.map(escaped _).mkString
- }
-}
+case class Printf(sourceInfo: SourceInfo, clk: Arg, pable: Printable) extends Command
case class Circuit(name: String, components: Seq[Component])