summaryrefslogtreecommitdiff
path: root/src/main
diff options
context:
space:
mode:
Diffstat (limited to 'src/main')
-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
3 files changed, 138 insertions, 15 deletions
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
+ }
}