From 0ed5eb48cdb916b644aaf9e5dbf48f6cfb6c60f4 Mon Sep 17 00:00:00 2001 From: Jack Koenig Date: Wed, 7 Sep 2016 14:18:29 -0700 Subject: 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--- .../scala/chisel3/internal/firrtl/Emitter.scala | 6 ++- src/main/scala/chisel3/package.scala | 51 ++++++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) (limited to 'src/main') diff --git a/src/main/scala/chisel3/internal/firrtl/Emitter.scala b/src/main/scala/chisel3/internal/firrtl/Emitter.scala index 8b94c68f..8ace27f9 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 + 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 0b548683..30e2b5c3 100644 --- a/src/main/scala/chisel3/package.scala +++ b/src/main/scala/chisel3/package.scala @@ -54,6 +54,27 @@ package object chisel3 { 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 class fromBigIntToLiteral(val x: BigInt) extends AnyVal { def U: UInt = UInt(x, Width()) @@ -79,4 +100,34 @@ package object chisel3 { def do_!= (that: BitPat)(implicit sourceInfo: SourceInfo): Bool = that != x def do_=/= (that: BitPat)(implicit sourceInfo: SourceInfo): Bool = that =/= x } + + /** 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) } -- cgit v1.2.3 From f793453ba6c4c42ef61eda3af8f04f7cadf80b95 Mon Sep 17 00:00:00 2001 From: jackkoenig Date: Wed, 7 Sep 2016 17:51:30 -0700 Subject: Fix bug in Printable FullName of submodule port Printable was using HasId.instanceName to get full names of Chisel nodes. instanceName uses the parent module of the HasId to get the Component to use in calling fullName on the underlying Ref. Unfortunately this means that any reference to a port of a instance will leave off the instance name. Fixing this required the following: - Add Component argument to Printable.unpack so that we can call Arg.fullName directly in the Printable - Pass the currently emitting module as the Component to Printable.unpack in the Emitter - Remove ability to create FullName Printables from Modules since the Module name is not known until after the printf is already emitted This commit also updates the PrintableSpec test to check that FullName and Decimal printing work on ports of instances --- src/main/scala/chisel3/internal/firrtl/Emitter.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main') diff --git a/src/main/scala/chisel3/internal/firrtl/Emitter.scala b/src/main/scala/chisel3/internal/firrtl/Emitter.scala index 8ace27f9..8849077d 100644 --- a/src/main/scala/chisel3/internal/firrtl/Emitter.scala +++ b/src/main/scala/chisel3/internal/firrtl/Emitter.scala @@ -26,7 +26,7 @@ private class Emitter(circuit: Circuit) { 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 => - val (fmt, args) = e.pable.unpack + val (fmt, args) = e.pable.unpack(ctx) val printfArgs = Seq(e.clk.fullName(ctx), "UInt<1>(1)", "\"" + printf.format(fmt) + "\"") ++ args printfArgs mkString ("printf(", ", ", ")") -- cgit v1.2.3 From bb240453abf96b4c2d75ebb2cdc7e3159068431d Mon Sep 17 00:00:00 2001 From: Henry Cook Date: Thu, 8 Sep 2016 13:02:22 -0700 Subject: Add IrrevocableIO alternative to DecoupledIO (#274) Add IrrevocableIO subclass of DecoupledIO that promises not to change .bits on a cycle after .valid is high and .ready is low--- src/main/scala/chisel3/util/Decoupled.scala | 99 ++++++++++++++++++++++++----- 1 file changed, 83 insertions(+), 16 deletions(-) (limited to 'src/main') diff --git a/src/main/scala/chisel3/util/Decoupled.scala b/src/main/scala/chisel3/util/Decoupled.scala index f37a5c31..51f5bd6a 100644 --- a/src/main/scala/chisel3/util/Decoupled.scala +++ b/src/main/scala/chisel3/util/Decoupled.scala @@ -7,23 +7,68 @@ package chisel3.util import chisel3._ -/** 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 = Bool(INPUT) val valid = Bool(OUTPUT) val bits = gen.cloneType.asOutput def fire(dummy: Int = 0): Bool = ready && valid - override def cloneType: this.type = new DecoupledIO(gen).asInstanceOf[this.type] } -/** Adds a ready-valid handshaking protocol to any interface. - * The standard used is that the consumer uses the flipped interface. +/** 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. */ -object Decoupled { +class DecoupledIO[+T <: Data](gen: T) extends ReadyValidIO[T](gen) +{ + override def cloneType: this.type = new DecoupledIO(gen).asInstanceOf[this.type] +} + +/** This factory adds a decoupled handshaking protocol to a data bundle. */ +object Decoupled +{ + /** Take any Data and wrap it in a DecoupledIO interface */ 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(irr.bits.dir == 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 + } +} + +/** 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) } + /** An I/O bundle for enqueuing data with valid/ready handshaking * Initialization must be handled, if necessary, by the parent circuit */ @@ -103,7 +148,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) @@ -164,22 +210,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(Decoupled(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] = { + /** 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)) 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) + val irr = Wire(new IrrevocableIO(deq.bits)) + irr.bits := deq.bits + irr.valid := deq.valid + deq.ready := irr.ready + irr + } } -- cgit v1.2.3 From 2ff229dac5f915e7f583cbf9cc8118674a4e52a5 Mon Sep 17 00:00:00 2001 From: Henry Cook Date: Tue, 13 Sep 2016 15:51:18 -0700 Subject: Bugfix: actually pass flow parameter from Queue factory to Queue module constructor --- src/main/scala/chisel3/util/Decoupled.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/main') diff --git a/src/main/scala/chisel3/util/Decoupled.scala b/src/main/scala/chisel3/util/Decoupled.scala index 51f5bd6a..b2634bec 100644 --- a/src/main/scala/chisel3/util/Decoupled.scala +++ b/src/main/scala/chisel3/util/Decoupled.scala @@ -225,7 +225,7 @@ object Queue entries: Int = 2, pipe: Boolean = false, flow: Boolean = false): DecoupledIO[T] = { - val q = Module(new Queue(enq.bits.cloneType, entries, pipe)) + 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 @@ -242,7 +242,7 @@ object Queue entries: Int = 2, pipe: Boolean = false, flow: Boolean = false): IrrevocableIO[T] = { - val deq = apply(enq, entries, pipe) + val deq = apply(enq, entries, pipe, flow) val irr = Wire(new IrrevocableIO(deq.bits)) irr.bits := deq.bits irr.valid := deq.valid -- cgit v1.2.3