summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJim Lawson2016-09-15 09:54:20 -0700
committerJim Lawson2016-09-15 09:54:20 -0700
commit36f77e86bbf1f471b158f36deae6a14f1d623075 (patch)
tree9b0060b03aa9c048645b0f624598adc93088d40e
parent19f5b7c6841bda318288990e643eb02fa22a49e2 (diff)
parent2ff229dac5f915e7f583cbf9cc8118674a4e52a5 (diff)
Merge branch 'master' into gsdt
-rw-r--r--chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala42
-rw-r--r--chiselFrontend/src/main/scala/chisel3/core/Bits.scala17
-rw-r--r--chiselFrontend/src/main/scala/chisel3/core/Data.scala7
-rw-r--r--chiselFrontend/src/main/scala/chisel3/core/Printable.scala152
-rw-r--r--chiselFrontend/src/main/scala/chisel3/core/Printf.scala42
-rw-r--r--chiselFrontend/src/main/scala/chisel3/internal/firrtl/IR.scala18
-rw-r--r--src/main/scala/chisel3/internal/firrtl/Emitter.scala6
-rw-r--r--src/main/scala/chisel3/package.scala53
-rw-r--r--src/main/scala/chisel3/util/Decoupled.scala94
-rw-r--r--src/test/scala/chiselTests/PrintableSpec.scala188
-rw-r--r--src/test/scala/chiselTests/Printf.scala9
11 files changed, 584 insertions, 44 deletions
diff --git a/chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala b/chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala
index 596a7244..6baf5202 100644
--- a/chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala
+++ b/chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala
@@ -48,10 +48,20 @@ object Vec {
// changing apply(elt0, elts*) to apply(elts*) causes a function collision
// with apply(Seq) after type erasure. Workarounds by either introducing a
// DummyImplicit or additional type parameter will break some code.
+
+ // Check that types are homogeneous. Width mismatch for Elements is safe.
require(!elts.isEmpty)
- val width = elts.map(_.width).reduce(_ max _)
- // If an element has a direction associated with it, use the bulk connect operator.
- val vec = Wire(new Vec(elts.head.cloneTypeWidth(width), elts.length))
+ def eltsCompatible(a: Data, b: Data) = a match {
+ case _: Element => a.getClass == b.getClass
+ case _: Aggregate => Mux.typesCompatible(a, b)
+ }
+
+ val t = elts.head
+ for (e <- elts.tail)
+ require(eltsCompatible(t, e), s"can't create Vec of heterogeneous types ${t.getClass} and ${e.getClass}")
+
+ val maxWidth = elts.map(_.width).reduce(_ max _)
+ val vec = Wire(new Vec(t.cloneTypeWidth(maxWidth), elts.length))
def doConnect(sink: T, source: T) = {
if (elts.head.flatten.exists(_.firrtlDirection != Direction.Unspecified)) {
sink bulkConnect source
@@ -202,6 +212,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
@@ -398,6 +419,21 @@ class Bundle extends Aggregate {
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 8a8721f9..8b3723f5 100644
--- a/chiselFrontend/src/main/scala/chisel3/core/Bits.scala
+++ b/chiselFrontend/src/main/scala/chisel3/core/Bits.scala
@@ -247,6 +247,7 @@ sealed abstract class Bits(width: Width, override val litArg: Option[LitArg])
def do_asSInt(implicit sourceInfo: SourceInfo): SInt
/** Reinterpret cast to Bits. */
+ @deprecated("Use asUInt, which does the same thing but returns a more concrete type", "chisel3")
final def asBits(): Bits = macro SourceInfoTransform.noArg
def do_asBits(implicit sourceInfo: SourceInfo): Bits = asUInt()
@@ -277,7 +278,7 @@ sealed abstract class Bits(width: Width, override val litArg: Option[LitArg])
pushOp(DefPrim(sourceInfo, UInt(w), ConcatOp, this.ref, that.ref))
}
- @deprecated("Use asBits, which makes the reinterpret cast more explicit and actually returns Bits", "chisel3")
+ @deprecated("Use asUInt, which does the same thing but makes the reinterpret cast more explicit", "chisel3")
override def toBits: UInt = do_asUInt(DeprecatedSourceInfo)
override def do_fromBits(that: Bits)(implicit sourceInfo: SourceInfo): this.type = {
@@ -285,6 +286,9 @@ sealed abstract class Bits(width: Width, override val litArg: Option[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.
@@ -827,10 +831,15 @@ object Mux {
pushOp(DefPrim(sourceInfo, d, MultiplexOp, cond.ref, con.ref, alt.ref))
}
+ private[core] def typesCompatible[T <: Data](x: T, y: T): Boolean = {
+ val sameTypes = x.getClass == y.getClass
+ val sameElements = x.flatten zip y.flatten forall { case (a, b) => a.getClass == b.getClass && a.width == b.width }
+ val sameNumElements = x.flatten.size == y.flatten.size
+ sameTypes && sameElements && sameNumElements
+ }
+
private def doAggregateMux[T <: Data](cond: Bool, con: T, alt: T)(implicit sourceInfo: SourceInfo): T = {
- require(con.getClass == alt.getClass, s"can't Mux between ${con.getClass} and ${alt.getClass}")
- for ((c, a) <- con.flatten zip alt.flatten)
- require(c.width == a.width, "can't Mux between aggregates of different width")
+ require(typesCompatible(con, alt), s"can't Mux between heterogeneous types ${con.getClass} and ${alt.getClass}")
doMux(cond, con, alt)
}
}
diff --git a/chiselFrontend/src/main/scala/chisel3/core/Data.scala b/chiselFrontend/src/main/scala/chisel3/core/Data.scala
index 991b1898..69c375f1 100644
--- a/chiselFrontend/src/main/scala/chisel3/core/Data.scala
+++ b/chiselFrontend/src/main/scala/chisel3/core/Data.scala
@@ -223,7 +223,7 @@ abstract class Data extends HasId {
*
* This performs the inverse operation of fromBits(Bits).
*/
- @deprecated("Use asBits, which makes the reinterpret cast more explicit and actually returns Bits", "chisel3")
+ @deprecated("Use asUInt, which does the same thing but makes the reinterpret cast more explicit", "chisel3")
def toBits(): UInt = SeqUtils.do_asUInt(this.flatten)(DeprecatedSourceInfo)
/** Reinterpret cast to UInt.
@@ -241,6 +241,8 @@ abstract class Data extends HasId {
// firrtlDirection is the direction we report to firrtl.
// It maintains the user-specified value (as opposed to the "actual" or applied/propagated value).
var firrtlDirection: Direction = Direction.Unspecified
+ /** Default pretty printing */
+ def toPrintable: Printable
}
object Wire {
@@ -293,4 +295,7 @@ sealed class Clock extends Element(Width(1)) {
case _: Clock => super.connect(that)(sourceInfo, connectCompileOptions)
case _ => super.badConnect(that)(sourceInfo)
}
+
+ /** 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..f6e63936
--- /dev/null
+++ b/chiselFrontend/src/main/scala/chisel3/core/Printable.scala
@@ -0,0 +1,152 @@
+// 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 Add support for names of Modules
+ * Currently impossible because unpack is called before the name is selected
+ * Could be implemented by adding a new format specifier to Firrtl (eg. %m)
+ * 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(ctx: Component): (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(ctx: Component): (String, Iterable[String]) = {
+ val (fmts, args) = pables.map(_ unpack ctx).unzip
+ (fmts.mkString, args.flatten)
+ }
+}
+/** Wrapper for printing Scala Strings */
+case class PString(str: String) extends Printable {
+ final def unpack(ctx: Component): (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(ctx: Component): (String, Iterable[String]) = {
+ (s"%$specifier", List(bits.ref.fullName(ctx)))
+ }
+}
+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(ctx: Component): (String, Iterable[String]) = (data.ref.name, List.empty)
+}
+/** Put full name within parent namespace (eg. bundleName.field) */
+case class FullName(data: Data) extends Printable {
+ final def unpack(ctx: Component): (String, Iterable[String]) = (data.ref.fullName(ctx), List.empty)
+}
+/** Represents escaped percents */
+case object Percent extends Printable {
+ final def unpack(ctx: Component): (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 400c144d..4ec13751 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) {
+ 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.forcedModule.reset) {
- printfWithoutReset(fmt, data:_*)
+ 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.forcedModule.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])
diff --git a/src/main/scala/chisel3/internal/firrtl/Emitter.scala b/src/main/scala/chisel3/internal/firrtl/Emitter.scala
index 8b94c68f..8849077d 100644
--- a/src/main/scala/chisel3/internal/firrtl/Emitter.scala
+++ b/src/main/scala/chisel3/internal/firrtl/Emitter.scala
@@ -25,7 +25,11 @@ private class Emitter(circuit: Circuit) {
case e: Connect => s"${e.loc.fullName(ctx)} <= ${e.exp.fullName(ctx)}"
case e: BulkConnect => s"${e.loc1.fullName(ctx)} <- ${e.loc2.fullName(ctx)}"
case e: Stop => s"stop(${e.clk.fullName(ctx)}, UInt<1>(1), ${e.ret})"
- case e: Printf => s"""printf(${e.clk.fullName(ctx)}, UInt<1>(1), "${e.format}"${e.ids.map(_.fullName(ctx)).fold(""){_ + ", " + _}})"""
+ case e: Printf =>
+ val (fmt, args) = e.pable.unpack(ctx)
+ val printfArgs = Seq(e.clk.fullName(ctx), "UInt<1>(1)",
+ "\"" + printf.format(fmt) + "\"") ++ args
+ printfArgs mkString ("printf(", ", ", ")")
case e: DefInvalid => s"${e.arg.fullName(ctx)} is invalid"
case e: DefInstance => s"inst ${e.name} of ${e.id.modName}"
case w: WhenBegin =>
diff --git a/src/main/scala/chisel3/package.scala b/src/main/scala/chisel3/package.scala
index 7911cb04..8f01779f 100644
--- a/src/main/scala/chisel3/package.scala
+++ b/src/main/scala/chisel3/package.scala
@@ -59,6 +59,58 @@ package object chisel3 { // scalastyle:ignore package.object.name
val when = chisel3.core.when
type WhenContext = chisel3.core.WhenContext
+ type Printable = chisel3.core.Printable
+ val Printable = chisel3.core.Printable
+ type Printables = chisel3.core.Printables
+ val Printables = chisel3.core.Printables
+ type PString = chisel3.core.PString
+ val PString = chisel3.core.PString
+ type FirrtlFormat = chisel3.core.FirrtlFormat
+ val FirrtlFormat = chisel3.core.FirrtlFormat
+ type Decimal = chisel3.core.Decimal
+ val Decimal = chisel3.core.Decimal
+ type Hexadecimal = chisel3.core.Hexadecimal
+ val Hexadecimal = chisel3.core.Hexadecimal
+ type Binary = chisel3.core.Binary
+ val Binary = chisel3.core.Binary
+ type Character = chisel3.core.Character
+ val Character = chisel3.core.Character
+ type Name = chisel3.core.Name
+ val Name = chisel3.core.Name
+ type FullName = chisel3.core.FullName
+ val FullName = chisel3.core.FullName
+ val Percent = chisel3.core.Percent
+
+ /** Implicit for custom Printable string interpolator */
+ implicit class PrintableHelper(val sc: StringContext) extends AnyVal {
+ /** Custom string interpolator for generating Printables: p"..."
+ * Will call .toString on any non-Printable arguments (mimicking s"...")
+ */
+ def p(args: Any*): Printable = {
+ sc.checkLengths(args) // Enforce sc.parts.size == pargs.size + 1
+ val pargs: Seq[Option[Printable]] = args map {
+ case p: Printable => Some(p)
+ case d: Data => Some(d.toPrintable)
+ case any => for {
+ v <- Option(any) // Handle null inputs
+ str = v.toString
+ if !str.isEmpty // Handle empty Strings
+ } yield PString(str)
+ }
+ val parts = sc.parts map StringContext.treatEscapes
+ // Zip sc.parts and pargs together ito flat Seq
+ // eg. Seq(sc.parts(0), pargs(0), sc.parts(1), pargs(1), ...)
+ val seq = for { // append None because sc.parts.size == pargs.size + 1
+ (literal, arg) <- parts zip (pargs :+ None)
+ optPable <- Seq(Some(PString(literal)), arg)
+ pable <- optPable // Remove Option[_]
+ } yield pable
+ Printables(seq)
+ }
+ }
+
+ implicit def string2Printable(str: String): Printable = PString(str)
+
/**
* These implicit classes allow one to convert scala.Int|scala.BigInt to
* Chisel.UInt|Chisel.SInt by calling .asUInt|.asSInt on them, respectively.
@@ -144,4 +196,5 @@ package object chisel3 { // scalastyle:ignore package.object.name
a.allElements
}
def getModulePorts(m: Module): Seq[Port] = m.getPorts
+ def getFirrtlDirection(d: Data): Direction = chisel3.core.Data.getFirrtlDirection(d)
}
diff --git a/src/main/scala/chisel3/util/Decoupled.scala b/src/main/scala/chisel3/util/Decoupled.scala
index 5ce2583c..d178cec5 100644
--- a/src/main/scala/chisel3/util/Decoupled.scala
+++ b/src/main/scala/chisel3/util/Decoupled.scala
@@ -8,13 +8,17 @@ package chisel3.util
import chisel3._
import chisel3.NotStrict.CompileOptions
-/** An I/O Bundle with simple handshaking using valid and ready signals for data 'bits'*/
-class DecoupledIO[+T <: Data](gen: T) extends Bundle
+/** An I/O Bundle containing 'valid' and 'ready' signals that handshake
+ * the transfer of data stored in the 'bits' subfield.
+ * The base protocol implied by the directionality is that the consumer
+ * uses the flipped interface. Actual semantics of ready/valid are
+ * enforced via use of concrete subclasses.
+ */
+abstract class ReadyValidIO[+T <: Data](gen: T) extends Bundle
{
val ready = Input(Bool())
val valid = Output(Bool())
val bits = Output(gen.chiselCloneType)
- override def cloneType: this.type = DecoupledIO(gen).asInstanceOf[this.type]
}
object DecoupledIO {
@@ -23,6 +27,19 @@ object DecoupledIO {
*/
def apply[T <: Data](gen: T): DecoupledIO[T] = new DecoupledIO(gen)
+ /** Take an IrrevocableIO and cast it to a DecoupledIO.
+ * This cast is only safe to do in cases where the IrrevocableIO
+ * is being produced as an output.
+ */
+ def apply[T <: Data](irr: IrrevocableIO[T]): DecoupledIO[T] = {
+ require(getFirrtlDirection(irr.bits) == OUTPUT, "Only safe to cast produced Irrevocable bits to Decoupled.")
+ val d = Wire(new DecoupledIO(irr.bits))
+ d.bits := irr.bits
+ d.valid := irr.valid
+ irr.ready := d.ready
+ d
+ }
+
implicit class AddMethodsToDecoupled[T<:Data](val target: DecoupledIO[T]) extends AnyVal {
def fire(): Bool = target.ready && target.valid
@@ -65,6 +82,33 @@ object DecoupledIO {
// }
}
+/** A concrete subclass of ReadyValidIO signalling that the user expects a
+ * "decoupled" interface: 'valid' indicates that the producer has
+ * put valid data in 'bits', and 'ready' indicates that the consumer is ready
+ * to accept the data this cycle. No requirements are placed on the signalling
+ * of ready or valid.
+ */
+class DecoupledIO[+T <: Data](gen: T) extends ReadyValidIO[T](gen)
+{
+ override def cloneType: this.type = new DecoupledIO(gen).asInstanceOf[this.type]
+}
+
+/** A concrete subclass of ReadyValidIO that promises to not change
+ * the value of 'bits' after a cycle where 'valid' is high and 'ready' is low.
+ * Additionally, once 'valid' is raised it will never be lowered until after
+ * 'ready' has also been raised.
+ */
+class IrrevocableIO[+T <: Data](gen: T) extends ReadyValidIO[T](gen)
+{
+ override def cloneType: this.type = new IrrevocableIO(gen).asInstanceOf[this.type]
+}
+
+/** Factory adds an irrevocable handshaking protocol to a data bundle. */
+object Irrevocable
+{
+ def apply[T <: Data](gen: T): IrrevocableIO[T] = new IrrevocableIO(gen)
+}
+
object EnqIO {
def apply[T<:Data](gen: T): DecoupledIO[T] = DecoupledIO(gen)
}
@@ -98,7 +142,8 @@ class QueueIO[T <: Data](gen: T, entries: Int) extends Bundle
* q.io.enq <> producer.io.out
* consumer.io.in <> q.io.deq }}}
*/
-class Queue[T <: Data](gen: T, val entries: Int,
+class Queue[T <: Data](gen: T,
+ val entries: Int,
pipe: Boolean = false,
flow: Boolean = false,
override_reset: Option[Bool] = None)
@@ -159,22 +204,43 @@ extends Module(override_reset=override_reset) {
}
}
-/** Generic hardware queue. Required parameter entries controls
- the depth of the queues. The width of the queue is determined
- from the inputs.
-
- Example usage:
- {{{ val q = Queue(DecoupledIO(UInt()), 16)
- q.io.enq <> producer.io.out
- consumer.io.in <> q.io.deq }}}
+/** Factory for a generic hardware queue. Required parameter 'entries' controls
+ * the depth of the queues. The width of the queue is determined
+ * from the input 'enq'.
+ *
+ * Example usage:
+ * {{{ consumer.io.in <> Queue(producer.io.out, 16) }}}
*/
object Queue
{
- def apply[T <: Data](enq: DecoupledIO[T], entries: Int = 2, pipe: Boolean = false): DecoupledIO[T] = {
- val q = Module(new Queue(enq.bits.chiselCloneType, entries, pipe))
+ /** Create a queue and supply a DecoupledIO containing the product. */
+ def apply[T <: Data](
+ enq: ReadyValidIO[T],
+ entries: Int = 2,
+ pipe: Boolean = false,
+ flow: Boolean = false): DecoupledIO[T] = {
+ val q = Module(new Queue(enq.bits.cloneType, entries, pipe, flow))
q.io.enq.valid := enq.valid // not using <> so that override is allowed
q.io.enq.bits := enq.bits
enq.ready := q.io.enq.ready
TransitName(q.io.deq, q)
}
+
+ /** Create a queue and supply a IrrevocableIO containing the product.
+ * Casting from Decoupled is safe here because we know the Queue has
+ * Irrevocable semantics; we didn't want to change the return type of
+ * apply() for backwards compatibility reasons.
+ */
+ def irrevocable[T <: Data](
+ enq: ReadyValidIO[T],
+ entries: Int = 2,
+ pipe: Boolean = false,
+ flow: Boolean = false): IrrevocableIO[T] = {
+ val deq = apply(enq, entries, pipe, flow)
+ val irr = Wire(new IrrevocableIO(deq.bits))
+ irr.bits := deq.bits
+ irr.valid := deq.valid
+ deq.ready := irr.ready
+ irr
+ }
}
diff --git a/src/test/scala/chiselTests/PrintableSpec.scala b/src/test/scala/chiselTests/PrintableSpec.scala
new file mode 100644
index 00000000..afef3c54
--- /dev/null
+++ b/src/test/scala/chiselTests/PrintableSpec.scala
@@ -0,0 +1,188 @@
+package chiselTests
+
+import org.scalatest.{FlatSpec, Matchers}
+import scala.collection.mutable
+
+import chisel3._
+import chisel3.testers.BasicTester
+import chisel3.Strict.CompileOptions
+
+/* Printable Tests */
+class PrintableSpec extends FlatSpec with Matchers {
+ private val PrintfRegex = """\s*printf\((.*)\).*""".r
+ // This regex is brittle, it relies on the first two arguments of the printf
+ // not containing quotes, problematic if Chisel were to emit UInt<1>("h01")
+ // instead of the current UInt<1>(1) for the enable signal
+ private val StringRegex = """([^"]*)"(.*?)"(.*)""".r
+ private case class Printf(str: String, args: Seq[String])
+ private def getPrintfs(firrtl: String): Seq[Printf] = {
+ def processArgs(str: String): Seq[String] =
+ str split "," map (_.trim) filter (_.nonEmpty)
+ def processBody(str: String): (String, Seq[String]) = {
+ str match {
+ case StringRegex(_, fmt, args) =>
+ (fmt, processArgs(args))
+ case _ => fail(s"Regex to process Printf should work on $str!")
+ }
+ }
+
+ firrtl split "\n" collect {
+ case PrintfRegex(matched) =>
+ val (str, args) = processBody(matched)
+ Printf(str, args)
+ }
+ }
+
+ behavior of "Printable & Custom Interpolator"
+
+ it should "pass exact strings through" in {
+ class MyModule extends BasicTester {
+ printf(p"An exact string")
+ }
+ val firrtl = Driver.emit(() => new MyModule)
+ getPrintfs(firrtl) match {
+ case Seq(Printf("An exact string", Seq())) =>
+ case e => fail()
+ }
+ }
+ it should "handle Printable and String concatination" in {
+ class MyModule extends BasicTester {
+ printf(p"First " + PString("Second ") + "Third")
+ }
+ val firrtl = Driver.emit(() => new MyModule)
+ getPrintfs(firrtl) match {
+ case Seq(Printf("First Second Third", Seq())) =>
+ case e => fail()
+ }
+ }
+ it should "call toString on non-Printable objects" in {
+ class MyModule extends BasicTester {
+ val myInt = 1234
+ printf(p"myInt = $myInt")
+ }
+ val firrtl = Driver.emit(() => new MyModule)
+ getPrintfs(firrtl) match {
+ case Seq(Printf("myInt = 1234", Seq())) =>
+ case e => fail()
+ }
+ }
+ it should "generate proper printf for simple Decimal printing" in {
+ class MyModule extends BasicTester {
+ val myWire = Wire(init = UInt(1234))
+ printf(p"myWire = ${Decimal(myWire)}")
+ }
+ val firrtl = Driver.emit(() => new MyModule)
+ getPrintfs(firrtl) match {
+ case Seq(Printf("myWire = %d", Seq("myWire"))) =>
+ case e => fail()
+ }
+ }
+ it should "handle printing literals" in {
+ class MyModule extends BasicTester {
+ printf(Decimal(UInt(10, 32)))
+ }
+ val firrtl = Driver.emit(() => new MyModule)
+ getPrintfs(firrtl) match {
+ case Seq(Printf("%d", Seq(lit))) =>
+ assert(lit contains "UInt<32>")
+ case e => fail()
+ }
+ }
+ it should "correctly escape percent" in {
+ class MyModule extends BasicTester {
+ printf(p"%")
+ }
+ val firrtl = Driver.emit(() => new MyModule)
+ getPrintfs(firrtl) match {
+ case Seq(Printf("%%", Seq())) =>
+ case e => fail()
+ }
+ }
+ it should "support names of circuit elements including submodule IO" in {
+ // Submodule IO is a subtle issue because the Chisel element has a different
+ // parent module
+ class MySubModule extends Module {
+ val io = new Bundle {
+ val fizz = UInt(width = 32)
+ }
+ }
+ class MyBundle extends Bundle {
+ val foo = UInt(width = 32)
+ override def cloneType = (new MyBundle).asInstanceOf[this.type]
+ }
+ class MyModule extends BasicTester {
+ override def desiredName = "MyModule"
+ val myWire = Wire(new MyBundle)
+ val myInst = Module(new MySubModule)
+ printf(p"${Name(myWire.foo)}")
+ printf(p"${FullName(myWire.foo)}")
+ printf(p"${FullName(myInst.io.fizz)}")
+ }
+ val firrtl = Driver.emit(() => new MyModule)
+ println(firrtl)
+ getPrintfs(firrtl) match {
+ case Seq(Printf("foo", Seq()),
+ Printf("myWire.foo", Seq()),
+ Printf("myInst.io.fizz", Seq())) =>
+ case e => fail()
+ }
+ }
+ it should "handle printing ports of submodules" in {
+ class MySubModule extends Module {
+ val io = new Bundle {
+ val fizz = UInt(width = 32)
+ }
+ }
+ class MyModule extends BasicTester {
+ val myInst = Module(new MySubModule)
+ printf(p"${myInst.io.fizz}")
+ }
+ val firrtl = Driver.emit(() => new MyModule)
+ getPrintfs(firrtl) match {
+ case Seq(Printf("%d", Seq("myInst.io.fizz"))) =>
+ case e => fail()
+ }
+ }
+ it should "print UInts and SInts as Decimal by default" in {
+ class MyModule extends BasicTester {
+ val myUInt = Wire(init = UInt(0))
+ val mySInt = Wire(init = SInt(-1))
+ printf(p"$myUInt & $mySInt")
+ }
+ val firrtl = Driver.emit(() => new MyModule)
+ getPrintfs(firrtl) match {
+ case Seq(Printf("%d & %d", Seq("myUInt", "mySInt"))) =>
+ case e => fail()
+ }
+ }
+ it should "print Vecs like Scala Seqs by default" in {
+ class MyModule extends BasicTester {
+ val myVec = Wire(Vec(4, UInt(width = 32)))
+ myVec foreach (_ := UInt(0))
+ printf(p"$myVec")
+ }
+ val firrtl = Driver.emit(() => new MyModule)
+ getPrintfs(firrtl) match {
+ case Seq(Printf("Vec(%d, %d, %d, %d)",
+ Seq("myVec[0]", "myVec[1]", "myVec[2]", "myVec[3]"))) =>
+ case e => fail()
+ }
+ }
+ it should "print Bundles like Scala Maps by default" in {
+ class MyModule extends BasicTester {
+ val myBun = Wire(new Bundle {
+ val foo = UInt(width = 32)
+ val bar = UInt(width = 32)
+ })
+ myBun.foo := UInt(0)
+ myBun.bar := UInt(0)
+ printf(p"$myBun")
+ }
+ val firrtl = Driver.emit(() => new MyModule)
+ getPrintfs(firrtl) match {
+ case Seq(Printf("Bundle(foo -> %d, bar -> %d)",
+ Seq("myBun.foo", "myBun.bar"))) =>
+ case e => fail()
+ }
+ }
+}
diff --git a/src/test/scala/chiselTests/Printf.scala b/src/test/scala/chiselTests/Printf.scala
index c872fde4..28b6132b 100644
--- a/src/test/scala/chiselTests/Printf.scala
+++ b/src/test/scala/chiselTests/Printf.scala
@@ -4,6 +4,7 @@ package chiselTests
import org.scalatest._
import chisel3._
+import chisel3.util._
import chisel3.testers.BasicTester
class SinglePrintfTester() extends BasicTester {
@@ -24,6 +25,11 @@ class MultiPrintfTester() extends BasicTester {
stop()
}
+class ASCIIPrintableTester extends BasicTester {
+ printf(PString((0x20 to 0x7e) map (_.toChar) mkString ""))
+ stop()
+}
+
class PrintfSpec extends ChiselFlatSpec {
"A printf with a single argument" should "run" in {
assertTesterPasses { new SinglePrintfTester }
@@ -34,4 +40,7 @@ class PrintfSpec extends ChiselFlatSpec {
"A printf with ASCII characters 1-127" should "run" in {
assertTesterPasses { new ASCIIPrintfTester }
}
+ "A printf with Printable ASCII characters 1-127" should "run" in {
+ assertTesterPasses { new ASCIIPrintableTester }
+ }
}