summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorducky2015-10-01 16:01:30 -0700
committerPalmer Dabbelt2015-10-19 14:59:51 -0700
commita5a3c3bd46b0d75442f126509633afb01440deab (patch)
treea782b67fa05fce789d1e3feeb4ca05258ed8329a /src
parentd2091505adb8b719f880d9e9ebaa1bbd17877b4c (diff)
Core.scala API documentation
Diffstat (limited to 'src')
-rw-r--r--src/main/scala/Chisel/Core.scala588
1 files changed, 510 insertions, 78 deletions
diff --git a/src/main/scala/Chisel/Core.scala b/src/main/scala/Chisel/Core.scala
index 9ec6f24d..217a5a72 100644
--- a/src/main/scala/Chisel/Core.scala
+++ b/src/main/scala/Chisel/Core.scala
@@ -14,17 +14,17 @@ object INPUT extends Direction("input") { def flip = OUTPUT }
object OUTPUT extends Direction("output") { def flip = INPUT }
object NO_DIR extends Direction("?") { def flip = NO_DIR }
+// REVIEW TODO: Should this actually be part of the RTL API? RTL should be
+// considered untouchable from a debugging standpoint?
object debug {
// TODO:
def apply (arg: Data) = arg
}
-/** *Data* is the root of the type system which includes
- Aggregate (Bundle, Vec) and Element (UInt, SInt, etc.).
-
- Instances of Data are meant to help with construction and correctness
- of a logic graph. They will trimmed out of the graph before a *Backend*
- generates target code.
+/** This forms the root of the type system for wire data types. The data value
+ * must be representable as some number (need not be known at Chisel compile
+ * time) of bits, and must have methods to pack / unpack structured data to /
+ * from bits.
*/
abstract class Data(dirArg: Direction) extends HasId {
def dir: Direction = dirVar
@@ -57,6 +57,9 @@ abstract class Data(dirArg: Direction) extends HasId {
private[Chisel] def cloneTypeWidth(width: Width): this.type
private[Chisel] def toType: String
+ // REVIEW TODO: Can these just be abstract, and left to implementing classes
+ // to define them (or even undefined)? Bonus: compiler can help you catch
+ // unimplemented functions.
def := (that: Data): Unit = this badConnect that
def <> (that: Data): Unit = this badConnect that
def cloneType: this.type
@@ -67,8 +70,24 @@ abstract class Data(dirArg: Direction) extends HasId {
def width: Width
final def getWidth = width.get
+ // REVIEW TODO: should this actually be part of the Data interface? this is
+ // an Aggregate function?
private[Chisel] def flatten: IndexedSeq[Bits]
+
+ /** Creates an new instance of this type, unpacking the input Bits into
+ * structured data. Generates no logic (should be either wires or a syntactic
+ * transformation).
+ *
+ * This performs the inverse operation of toBits.
+ *
+ * @note does NOT assign to the object this is called on, instead creating a
+ * NEW object
+ */
def fromBits(n: Bits): this.type = {
+ // REVIEW TODO: width match checking?
+ // REVIEW TODO: perhaps have a assign version, especially since this is
+ // called from a specific object, instead of a factory constructor. It's
+ // not immediately obvious that this creates a new object.
var i = 0
val wire = Wire(this.cloneType)
for (x <- wire.flatten) {
@@ -77,6 +96,12 @@ abstract class Data(dirArg: Direction) extends HasId {
}
wire.asInstanceOf[this.type]
}
+
+ /** Packs the value of this object as plain Bits. Generates no logic (should
+ * be either wires or a syntactic transformation).
+ *
+ * This performs the inverse operation of fromBits(Bits).
+ */
def toBits(): UInt = Cat(this.flatten.reverse)
}
@@ -92,7 +117,6 @@ object Wire {
}
}
-/** A factory for Reg objects. */
object Reg {
private[Chisel] def makeType[T <: Data](t: T = null, next: T = null, init: T = null): T = {
if (t ne null) t.cloneType
@@ -104,7 +128,17 @@ object Reg {
} else throwException("cannot infer type")
}
+ /** Creates a register with optional next and initialization values.
+ *
+ * @param t: data type for the register
+ * @param next: new value register is to be updated with every cycle (or
+ * empty to not update unless assigned to using the := operator)
+ * @param init: initialization value on reset (or empty for uninitialized,
+ * where the register value persists across a reset)
+ */
def apply[T <: Data](t: T = null, next: T = null, init: T = null): T = {
+ // REVIEW TODO: rewrite this in a less brittle way, perhaps also in a way
+ // that doesn't need two implementations of apply()
val x = makeType(t, next, init)
pushCommand(DefRegister(x, Node(x._parent.get.clock), Node(x._parent.get.reset))) // TODO multi-clock
if (init != null)
@@ -113,6 +147,12 @@ object Reg {
x := next
x
}
+
+ /** Creates a register without initialization (reset is ignored). Value does
+ * not change unless assigned to (using the := operator).
+ *
+ * @param outType: data type for the register
+ */
def apply[T <: Data](outType: T): T = Reg[T](outType, null.asInstanceOf[T], null.asInstanceOf[T])
}
@@ -120,6 +160,11 @@ object Mem {
@deprecated("Chisel3 Mem argument order should be size, t - this will be removed by Chisel3 official release", "now")
def apply[T <: Data](t: T, size: Int): Mem[T] = apply(size, t)
+ /** Creates a combinational-read, sequential-write [[Mem]].
+ *
+ * @param size number of elements in the memory
+ * @param t data type of memory element
+ */
def apply[T <: Data](size: Int, t: T): Mem[T] = {
val mt = t.cloneType
val mem = new Mem(mt, size)
@@ -129,25 +174,63 @@ object Mem {
}
sealed abstract class MemBase[T <: Data](t: T, val length: Int) extends HasId with VecLike[T] {
+ // REVIEW TODO: make accessors (static/dynamic, read/write) combinations consistent.
+
+ /** Creates a read accessor into the memory with static addressing. See the
+ * class documentation of the memory for more detailed information.
+ */
def apply(idx: Int): T = apply(UInt(idx))
+
+ /** Creates a read accessor into the memory with dynamic addressing. See the
+ * class documentation of the memory for more detailed information.
+ */
def apply(idx: UInt): T =
pushCommand(DefAccessor(t.cloneType, Node(this), NO_DIR, idx.ref)).id
def read(idx: UInt): T = apply(idx)
+
+ /** Creates a write accessor into the memory.
+ *
+ * @param idx memory element index to write into
+ * @param data new data to write
+ */
def write(idx: UInt, data: T): Unit = apply(idx) := data
+
+ /** Creates a masked write accessor into the memory.
+ *
+ * @param idx memory element index to write into
+ * @param data new data to write
+ * @param mask write mask as a Vec of Bool: a write to the Vec element in
+ * memory is only performed if the corresponding mask index is true.
+ *
+ * @note this is only allowed if the memory's element data type is a Vec
+ */
def write(idx: UInt, data: T, mask: Vec[Bool]) (implicit evidence: T <:< Vec[_]): Unit = {
+ // REVIEW TODO: error checking to detect zip length mismatch?
+
val accessor = apply(idx).asInstanceOf[Vec[Data]]
for (((cond, port), datum) <- mask zip accessor zip data.asInstanceOf[Vec[Data]])
when (cond) { port := datum }
}
}
+/** A combinational-read, sequential-write memory.
+ *
+ * Writes take effect on the rising clock edge after the request. Reads are
+ * combinational (requests will return data on the same cycle).
+ * Read-after-write hazards are not an issue.
+ */
sealed class Mem[T <: Data](t: T, length: Int) extends MemBase(t, length)
object SeqMem {
@deprecated("Chisel3 SeqMem argument order should be size, t - this will be removed by Chisel3 official release", "now")
def apply[T <: Data](t: T, size: Int): SeqMem[T] = apply(size, t)
+ /** Creates a sequential-read, sequential-write [[SeqMem]].
+ *
+ * @param size number of elements in the memory
+ * @param t data type of memory element
+ */
def apply[T <: Data](size: Int, t: T): SeqMem[T] = {
val mt = t.cloneType
val mem = new SeqMem(mt, size)
@@ -156,19 +239,39 @@ object SeqMem {
}
}
+/** A sequential-read, sequential-write memory.
+ *
+ * Writes take effect on the rising clock edge after the request. Reads return
+ * data on the rising edge after the request. Read-after-write behavior (when
+ * a read and write to the same address are requested on the same cycle) is
+ * undefined.
+ */
sealed class SeqMem[T <: Data](t: T, n: Int) extends MemBase[T](t, n) {
def read(addr: UInt, enable: Bool): T =
read(Mux(enable, addr, Poison(addr)))
}
object Vec {
+ /** Creates a new [[Vec]] with `n` entries of the specified data type.
+ *
+ * @note elements are NOT assigned by default and have no value
+ */
def apply[T <: Data](n: Int, gen: T): Vec[T] = new Vec(gen.cloneType, n)
@deprecated("Chisel3 vec argument order should be n, gen - this will be removed by Chisel3 official release", "now")
def apply[T <: Data](gen: T, n: Int): Vec[T] = new Vec(gen.cloneType, n)
- /** Returns a new *Vec* from a sequence of *Data* nodes.
+
+ /** Creates a new [[Vec]] composed of elements of the input Seq of [[Data]]
+ * nodes.
+ *
+ * @note input elements should be of the same type
+ * @note the width of all output elements is the width of the largest input
+ * element
+ * @note output elements are connected from the input elements
*/
def apply[T <: Data](elts: Seq[T]): Vec[T] = {
+ // REVIEW TODO: error checking to guard against type mismatch?
+
require(!elts.isEmpty)
val width = elts.map(_.width).reduce(_ max _)
val vec = new Vec(elts.head.cloneTypeWidth(width), elts.length)
@@ -177,31 +280,57 @@ object Vec {
v := e
vec
}
- /** Returns a new *Vec* from the concatenation of a *Data* node
- and a sequence of *Data* nodes.
+
+ /** Creates a new [[Vec]] composed of the input [[Data]] nodes.
+ *
+ * @note input elements should be of the same type
+ * @note the width of all output elements is the width of the largest input
+ * element
+ * @note output elements are connected from the input elements
*/
def apply[T <: Data](elt0: T, elts: T*): Vec[T] =
+ // REVIEW TODO: does this really need to exist as a standard function?
apply(elt0 +: elts.toSeq)
- /** Returns an array containing values of a given function over
- a range of integer values starting from 0.
+
+ /** Creates a new [[Vec]] of length `n` composed of the results of the given
+ * function applied over a range of integer values starting from 0.
+ *
+ * @param n number of elements in the vector (the function is applied from
+ * 0 to `n-1`)
+ * @param gen function that takes in an Int (the index) and returns a
+ * [[Data]] that becomes the output element
*/
- def tabulate[T <: Data](n: Int)(gen: (Int) => T): Vec[T] =
+ def tabulate[T <: Data](n: Int)(gen: (Int) => T): Vec[T] =
apply((0 until n).map(i => gen(i)))
- /** Returns an array that contains the results of some element computation
- a number of times.
- Note that this means that elem is computed a total of n times.
+ /** Creates a new [[Vec]] of length `n` composed of the result of the given
+ * function repeatedly applied.
+ *
+ * @param n number of elements (amd the number of times the function is
+ * called)
+ * @param gen function that generates the [[Data]] that becomes the output
+ * element
*/
def fill[T <: Data](n: Int)(gen: => T): Vec[T] = apply(Seq.fill(n)(gen))
}
+/** An abstract class for data types that solely consist of (are an aggregate
+ * of) other Data objects.
+ */
sealed abstract class Aggregate(dirArg: Direction) extends Data(dirArg) {
private[Chisel] def cloneTypeWidth(width: Width): this.type = cloneType
def width: Width = flatten.map(_.width).reduce(_ + _)
}
+/** A vector (array) of [[Data]] elements. Provides hardware versions of various
+ * collection transformation functions found in software array implementations.
+ *
+ * @tparam T type of elements
+ */
sealed class Vec[T <: Data] private (gen: => T, val length: Int)
extends Aggregate(gen.dir) with VecLike[T] {
+ // REVIEW TODO: should this take a Seq instead of a gen()?
+
private val self = IndexedSeq.fill(length)(gen)
override def <> (that: Data): Unit = that match {
@@ -210,10 +339,13 @@ sealed class Vec[T <: Data] private (gen: => T, val length: Int)
}
def <> (that: Seq[T]): Unit =
+ // REVIEW TODO: come up with common style: match on type in body or
+ // multiple invocation signatures
for ((a, b) <- this zip that)
a <> b
def <> (that: Vec[T]): Unit = this bulkConnect that
+ // REVIEW TODO: standardize as above
override def := (that: Data): Unit = that match {
case _: Vec[_] => this connect that
@@ -221,6 +353,7 @@ sealed class Vec[T <: Data] private (gen: => T, val length: Int)
}
def := (that: Seq[T]): Unit = {
+ // REVIEW TODO: standardize as above
require(this.length == that.length)
for ((a, b) <- this zip that)
a := b
@@ -228,14 +361,25 @@ sealed class Vec[T <: Data] private (gen: => T, val length: Int)
def := (that: Vec[T]): Unit = this connect that
+ /** Creates a dynamically indexed read accessor into the array. Generates
+ * logic (likely some kind of multiplexer).
+ */
def apply(idx: UInt): T = {
val x = gen
+ // REVIEW TODO: what happens when people try to assign into this?
+ // Should this be a read-only reference?
pushCommand(DefAccessor(x, Node(this), NO_DIR, idx.ref))
x
}
+ /** Creates a statically indexed read accessor into the array. Generates no
+ * logic.
+ */
def apply(idx: Int): T = self(idx)
+
def read(idx: UInt): T = apply(idx)
+ // REVIEW TODO: does this need to exist?
+
def write(idx: UInt, data: T): Unit = apply(idx) := data
override def cloneType: this.type =
@@ -250,26 +394,86 @@ sealed class Vec[T <: Data] private (gen: => T, val length: Int)
elt.setRef(this, i)
}
+/** A trait for [[Vec]]s containing common hardware generators for collection
+ * operations.
+ */
trait VecLike[T <: Data] extends collection.IndexedSeq[T] {
def read(idx: UInt): T
+ // REVIEW TODO: does this need to exist? (does the same thing as apply)
+
def write(idx: UInt, data: T): Unit
def apply(idx: UInt): T
- def forall(p: T => Bool): Bool = (this map p).fold(Bool(true))(_&&_)
- def exists(p: T => Bool): Bool = (this map p).fold(Bool(false))(_||_)
- def contains(x: T) (implicit evidence: T <:< UInt): Bool = this.exists(_ === x)
+ /** Outputs true if p outputs true for every element.
+ *
+ * This generates into a function evaluation followed by a logical AND
+ * reduction.
+ */
+ def forall(p: T => Bool): Bool = (this map p).fold(Bool(true))(_ && _)
+
+ /** Outputs true if p outputs true for at least one element.
+ *
+ * This generates into a function evaluation followed by a logical OR
+ * reduction.
+ */
+ def exists(p: T => Bool): Bool = (this map p).fold(Bool(false))(_ || _)
+
+ /** Outputs true if the vector contains at least one element equal to x (using
+ * the === operator).
+ *
+ * This generates into an equality comparison followed by a logical OR
+ * reduction.
+ */
+ def contains(x: T)(implicit evidence: T <:< UInt): Bool = this.exists(_ === x)
+
+ /** Outputs the number of elements for which p is true.
+ *
+ * This generates into a function evaluation followed by a set bit counter.
+ */
def count(p: T => Bool): UInt = PopCount((this map p).toSeq)
+ /** Helper function that appends an index (literal value) to each element,
+ * useful for hardware generators which output an index.
+ */
private def indexWhereHelper(p: T => Bool) = this map p zip (0 until length).map(i => UInt(i))
+
+ /** Outputs the index of the first element for which p outputs true.
+ *
+ * This generates into a function evaluation followed by a priority mux.
+ */
def indexWhere(p: T => Bool): UInt = PriorityMux(indexWhereHelper(p))
+
+ /** Outputs the index of the last element for which p outputs true.
+ *
+ * This generates into a function evaluation followed by a priority mux.
+ */
def lastIndexWhere(p: T => Bool): UInt = PriorityMux(indexWhereHelper(p).reverse)
+
+ /** Outputs the index of the element for which p outputs true, assuming that
+ * the there is exactly one such element.
+ *
+ * This generates into a function evaluation followed by a one-hot mux. The
+ * implementation may be more efficient than a priority mux, but incorrect
+ * results are possible if there is not exactly one true element.
+ */
def onlyIndexWhere(p: T => Bool): UInt = Mux1H(indexWhereHelper(p))
+ // REVIEW TODO: can (should?) this be assertion checked?
}
-/** A bit pattern object to enable representation of dont cares */
object BitPat {
+ /** Parses a bit pattern string into (bits, mask, width).
+ *
+ * @return bits the literal value, with don't cares being 0
+ * @return mask the mask bits, with don't cares being 0 and cares being 1
+ * @return width the number of bits in the literal, including values and
+ * don't cares.
+ */
private def parse(x: String): (BigInt, BigInt, Int) = {
- require(x.head == 'b', "BINARY BitPats ONLY")
+ // REVIEW TODO: can this be merged with literal parsing creating one unified
+ // Chisel string to value decoder (which can also be invoked by libraries
+ // and testbenches?
+ // REVIEW TODO: Verilog Xs also handle octal and hex cases.
+ require(x.head == 'b', "BitPats must be in binary and be prefixed with 'b'")
var bits = BigInt(0)
var mask = BigInt(0)
for (d <- x.tail) {
@@ -279,50 +483,67 @@ object BitPat {
bits = (bits << 1) + (if (d == '1') 1 else 0)
}
}
- (bits, mask, x.length-1)
+ (bits, mask, x.length - 1)
}
- /** Get a bit pattern from a string
- * @param n a string with format b---- eg) b1?01
- * @note legal characters are 0, 1, ? and must be base 2*/
+ /** Creates a [[BitPat]] literal from a string.
+ *
+ * @param n the literal value as a string, in binary, prefixed with 'b'
+ * @note legal characters are '0', '1', and '?', as well as '_' as white
+ * space (which are ignored)
+ */
def apply(n: String): BitPat = {
val (bits, mask, width) = parse(n)
new BitPat(bits, mask, width)
}
- /** Get a bit pattern of don't cares with a specified width */
+ /** Creates a [[BitPat]] of all don't cares of a specified width. */
+ // REVIEW TODO: is this really necessary? if so, can there be a better name?
def DC(width: Int): BitPat = BitPat("b" + ("?" * width))
// BitPat <-> UInt
/** enable conversion of a bit pattern to a UInt */
+ // REVIEW TODO: Doesn't having a BitPat with all mask bits high defeat the
+ // point of using a BitPat in the first place?
implicit def BitPatToUInt(x: BitPat): UInt = {
- require(x.mask == (BigInt(1) << x.getWidth)-1)
+ require(x.mask == (BigInt(1) << x.getWidth) - 1)
UInt(x.value, x.getWidth)
}
+
/** create a bit pattern from a UInt */
+ // REVIEW TODO: Similar, what is the point of this?
implicit def apply(x: UInt): BitPat = {
require(x.isLit)
BitPat("b" + x.litValue.toString(2))
}
}
-/** A class to create bit patterns
- * Use the [[Chisel.BitPat$ BitPat]] object instead of this class directly */
+// TODO: Break out of Core? (this doesn't involve FIRRTL generation)
+/** Bit patterns are literals with masks, used to represent values with don't
+ * cares. Equality comparisons will ignore don't care bits (for example,
+ * BitPat(0b10?1) === UInt(0b1001) and UInt(0b1011)).
+ */
sealed class BitPat(val value: BigInt, val mask: BigInt, width: Int) {
def getWidth: Int = width
def === (other: UInt): Bool = UInt(value) === (other & UInt(mask))
def != (other: UInt): Bool = !(this === other)
}
+/** Element is a leaf data type: it cannot contain other Data objects. Example
+ * uses are for representing primitive data types, like integers and bits.
+ */
abstract class Element(dirArg: Direction, val width: Width) extends Data(dirArg) {
+ // REVIEW TODO: toBits is implemented in terms of flatten... inheriting this
+ // without rewriting toBits will break things. Perhaps have a specific element
+ // API?
private[Chisel] def flatten: IndexedSeq[UInt] = IndexedSeq(toBits)
}
-/** Create a new Clock */
object Clock {
def apply(dir: Direction = NO_DIR): Clock = new Clock(dir)
}
+// TODO: Document this.
sealed class Clock(dirArg: Direction) extends Element(dirArg, Width(1)) {
def cloneType: this.type = Clock(dirArg).asInstanceOf[this.type]
private[Chisel] override def flatten: IndexedSeq[UInt] = IndexedSeq()
@@ -335,35 +556,62 @@ sealed class Clock(dirArg: Direction) extends Element(dirArg, Width(1)) {
}
}
+/** A data type for values represented by a single bitvector. Provides basic
+ * bitwise operations.
+ */
sealed abstract class Bits(dirArg: Direction, width: Width, override val litArg: Option[LitArg]) extends Element(dirArg, width) {
+ // REVIEW TODO: should this be abstract? It may be good to use Bits for values
+ // where you don't need artihmetic operations / arithmetic doesn't make sense
+ // like opcodes and stuff.
+
+ // REVIEW TODO: Why do we need a fromInt? Why does it drop the argument?
def fromInt(x: BigInt): this.type
+ // REVIEW TODO: purpose of dedicated lit logic?
def makeLit(value: BigInt): LitArg
def cloneType: this.type = cloneTypeWidth(width)
override def <> (that: Data): Unit = this := that
- /** Extract a single Bool at index *bit*.
+ /** Returns the specified bit on this wire as a [[Bool]], statically
+ * addressed. Generates no logic.
*/
+ // REVIEW TODO: Ddeduplicate constructor with apply(Int)
final def apply(x: BigInt): Bool = {
if (x < 0)
Builder.error(s"Negative bit indices are illegal (got $x)")
if (isLit()) Bool(((litValue() >> x.toInt) & 1) == 1)
else pushOp(DefPrim(Bool(), BitSelectOp, this.ref, ILit(x)))
}
+
final def apply(x: Int): Bool =
apply(BigInt(x))
+
+ /** Returns the specified bit on this wire as a [[Bool]], dynamically
+ * addressed. Generates logic: implemented as a variable shifter.
+ */
final def apply(x: UInt): Bool =
(this >> x)(0)
- /** Extract a range of bits, inclusive from hi to lo
- * {{{ myBits = 0x5, myBits(1,0) => 0x1 }}} */
+ /** Returns a subset of bits on this wire from `hi` to `lo` (inclusive),
+ * statically addressed. Generates no logic.
+ *
+ * @example
+ * {{{
+ * myBits = 0x5 = 0b101
+ * myBits(1,0) => 0b01 // extracts the two least significant bits
+ * }}}
+ */
final def apply(x: Int, y: Int): UInt = {
if (x < y || y < 0)
Builder.error(s"Invalid bit range ($x,$y)")
+ // REVIEW TODO: should we support negative indexing Python style, at least
+ // where widths are known?
val w = x - y + 1
if (isLit()) UInt((litValue >> y) & ((BigInt(1) << w) - 1), w)
else pushOp(DefPrim(UInt(width = w), BitsExtractOp, this.ref, ILit(x), ILit(y)))
}
+
+ // REVIEW TODO: again, is this necessary? Or just have this and use implicits?
final def apply(x: BigInt, y: BigInt): UInt = apply(x.toInt, y.toInt)
private[Chisel] def unop[T <: Data](dest: T, op: PrimOp): T =
@@ -377,22 +625,63 @@ sealed abstract class Bits(dirArg: Direction, width: Width, override val litArg:
private[Chisel] def redop(op: PrimOp): Bool =
pushOp(DefPrim(Bool(), op, this.ref))
- /** invert all bits with ~ */
+ /** Returns this wire bitwise-inverted. */
def unary_~ : this.type = unop(cloneTypeWidth(width), BitNotOp)
+
+ /** Returns this wire zero padded up to the specified width.
+ *
+ * @note for SInts only, this does sign extension
+ */
def pad (other: Int): this.type = binop(cloneTypeWidth(this.width max Width(other)), PadOp, other)
/** Shift left operation */
+ // REVIEW TODO: redundant
+ // REVIEW TODO: should these return this.type or Bits?
def << (other: BigInt): Bits
+
+ /** Returns this wire statically left shifted by the specified amount,
+ * inserting zeros into the least significant bits.
+ *
+ * The width of the output is `other` larger than the input. Generates no
+ * logic.
+ */
def << (other: Int): Bits
+
+ /** Returns this wire dynamically left shifted by the specified amount,
+ * inserting zeros into the least significant bits.
+ *
+ * The width of the output is `pow(2, width(other))` larger than the input.
+ * Generates a dynamic shifter.
+ */
def << (other: UInt): Bits
+
/** Shift right operation */
+ // REVIEW TODO: redundant
def >> (other: BigInt): Bits
+
+ /** Returns this wire statically right shifted by the specified amount,
+ * inserting zeros into the most significant bits.
+ *
+ * The width of the output is the same as the input. Generates no logic.
+ */
def >> (other: Int): Bits
+
+ /** Returns this wire dynamically right shifted by the specified amount,
+ * inserting zeros into the most significant bits.
+ *
+ * The width of the output is the same as the input. Generates a dynamic
+ * shifter.
+ */
def >> (other: UInt): Bits
- /** Split up this bits instantiation to a Vec of Bools */
+ /** Returns the contents of this wire as a [[Vec]] of [[Bool]]s. Generates no
+ * logic.
+ */
def toBools: Vec[Bool] = Vec.tabulate(this.getWidth)(i => this(i))
+ // REVIEW TODO: is this appropriate here? Should this be a (implicit?) cast in
+ // the SInt object instead? Bits shouldn't know about UInt/SInt, which are
+ // downstream?
def asSInt(): SInt
def asUInt(): UInt
final def toSInt(): SInt = asSInt
@@ -403,9 +692,17 @@ sealed abstract class Bits(dirArg: Direction, width: Width, override val litArg:
case _ => throwException(s"can't covert UInt<$width> to Bool")
}
- /** Cat bits together to into a single data object with the width of both combined */
+ // REVIEW TODO: where did this syntax come from?
+ /** Returns this wire concatenated with `other`, where this wire forms the
+ * most significant part and `other` forms the least significant part.
+ *
+ * The width of the output is sum of the inputs. Generates no logic.
+ */
def ## (other: Bits): UInt = Cat(this, other)
+
+ // REVIEW TODO: This just _looks_ wrong.
override def toBits = asUInt
+
override def fromBits(n: Bits): this.type = {
val res = Wire(this).asInstanceOf[this.type]
res := n
@@ -413,24 +710,75 @@ sealed abstract class Bits(dirArg: Direction, width: Width, override val litArg:
}
}
+// REVIEW TODO: Numeric (strictly UInt/SInt/float) or numeric-like (complex,
+// etc)? First is easy to define, second not so much. Perhaps rename IntLike?
+// Also should add intended purpose.
+/** Abstract trait defining operations available on numeric-like wire data
+ * types.
+ */
abstract trait Num[T <: Data] {
// def << (b: T): T;
// def >> (b: T): T;
//def unary_-(): T;
+
+ // REVIEW TODO: double check ops conventions against FIRRTL
+
+ /** Outputs the sum of `this` and `b`. The resulting width is the max of the
+ * operands plus 1 (should not overflow).
+ */
def + (b: T): T;
+
+ /** Outputs the product of `this` and `b`. The resulting width is the sum of
+ * the operands.
+ *
+ * @note can generate a single-cycle multiplier, which can result in
+ * significant cycle time and area costs
+ */
def * (b: T): T;
+
+ /** Outputs the quotient of `this` and `b`.
+ *
+ * TODO: full rules
+ */
def / (b: T): T;
+
def % (b: T): T;
+
+ /** Outputs the difference of `this` and `b`. The resulting width is the max
+ * of the operands plus 1 (should not overflow).
+ */
def - (b: T): T;
+
+ /** Outputs true if `this` < `b`.
+ */
def < (b: T): Bool;
+
+ /** Outputs true if `this` <= `b`.
+ */
def <= (b: T): Bool;
+
+ /** Outputs true if `this` > `b`.
+ */
def > (b: T): Bool;
+
+ /** Outputs true if `this` >= `b`.
+ */
def >= (b: T): Bool;
+ /** Outputs the minimum of `this` and `b`. The resulting width is the max of
+ * the operands. Generates a comparison followed by a mux.
+ */
def min(b: T): T = Mux(this < b, this.asInstanceOf[T], b)
+
+ /** Outputs the maximum of `this` and `b`. The resulting width is the max of
+ * the operands. Generates a comparison followed by a mux.
+ */
def max(b: T): T = Mux(this < b, b, this.asInstanceOf[T])
}
+/** A data type for unsigned integers, represented as a binary bitvector.
+ * Defines arithmetic operations between other integer types.
+ */
sealed class UInt private[Chisel] (dir: Direction, width: Width, lit: Option[ULit] = None) extends Bits(dir, width, lit) with Num[UInt] {
private[Chisel] override def cloneTypeWidth(w: Width): this.type =
new UInt(dir, w).asInstanceOf[this.type]
@@ -444,6 +792,7 @@ sealed class UInt private[Chisel] (dir: Direction, width: Width, lit: Option[ULi
case _ => this badConnect that
}
+ // TODO: refactor to share documentation with Num or add independent scaladoc
def unary_- = UInt(0) - this
def unary_-% = UInt(0) -% this
def +& (other: UInt): UInt = binop(UInt((this.width max other.width) + 1), AddOp, other)
@@ -461,6 +810,7 @@ sealed class UInt private[Chisel] (dir: Direction, width: Width, lit: Option[ULi
def | (other: UInt): UInt = binop(UInt(this.width max other.width), BitOrOp, other)
def ^ (other: UInt): UInt = binop(UInt(this.width max other.width), BitXorOp, other)
+ // REVIEW TODO: Can this be defined on Bits?
def orR = this != UInt(0)
def andR = ~this === UInt(0)
def xorR = redop(XorReduceOp)
@@ -473,6 +823,7 @@ sealed class UInt private[Chisel] (dir: Direction, width: Width, lit: Option[ULi
def === (other: UInt): Bool = compop(EqualOp, other)
def unary_! : Bool = this === Bits(0)
+ // REVIEW TODO: Can these also not be defined on Bits?
def << (other: Int): UInt = binop(UInt(this.width + other), ShiftLeftOp, other)
def << (other: BigInt): UInt = this << other.toInt
def << (other: UInt): UInt = binop(UInt(this.width.dynamicShiftLeft(other.width)), DynamicShiftLeftOp, other)
@@ -488,12 +839,23 @@ sealed class UInt private[Chisel] (dir: Direction, width: Width, lit: Option[ULi
def === (that: BitPat): Bool = that === this
def != (that: BitPat): Bool = that != this
- /** Convert a UInt to an SInt by added a MSB zero */
+ // REVIEW TODO: Is this really the common definition of zero extend?
+ // Can we just define UInt/SInt constructors on Bits as a reinterpret case?
+ /** Returns this UInt as a [[SInt]] with an additional zero in the MSB.
+ */
def zext(): SInt = pushOp(DefPrim(SInt(width + 1), ConvertOp, ref))
+
+ /** Returns this UInt as a [[SInt]], without changing width or bit value. The
+ * SInt is not guaranteed to have the same value (for example, if the MSB is
+ * high, it will be interpreted as a negative value).
+ */
def asSInt(): SInt = pushOp(DefPrim(SInt(width), AsSIntOp, ref))
+
def asUInt(): UInt = this
}
+// REVIEW TODO: why not just have this be a companion object? Why the trait
+// instead of object UInt?
sealed trait UIntFactory {
/** Create a UInt type with inferred width. */
def apply(): UInt = apply(NO_DIR, Width())
@@ -539,9 +901,10 @@ sealed trait UIntFactory {
else Width()
}
-/** Provides a set of operations to create UInt types and literals. */
object UInt extends UIntFactory
+// REVIEW TODO: Wait, wha?! Why does this exist? Things should be DRY and
+// unambiguous.
/** Provides a set of operations to create UInt types and literals.
* Identical in functionality to the UInt companion object. */
object Bits extends UIntFactory
@@ -601,7 +964,6 @@ sealed class SInt private (dir: Direction, width: Width, lit: Option[SLit] = Non
def asSInt(): SInt = this
}
-/** Provides a set of operations to create SInt types and literals. */
object SInt {
/** Create an SInt type with inferred width. */
def apply(): SInt = apply(NO_DIR, Width())
@@ -626,6 +988,10 @@ object SInt {
}
}
+// REVIEW TODO: Why does this extend UInt and not Bits? Does defining airth
+// operations on a Bool make sense?
+/** A data type for booleans, defined as a single bit indicating true or false.
+ */
sealed class Bool(dir: Direction, lit: Option[ULit] = None) extends UInt(dir, Width(1), lit) {
private[Chisel] override def cloneTypeWidth(w: Width): this.type = {
require(!w.known || w.get == 1)
@@ -637,26 +1003,43 @@ sealed class Bool(dir: Direction, lit: Option[ULit] = None) extends UInt(dir, Wi
Bool(value == 1).asInstanceOf[this.type]
}
+ // REVIEW TODO: Why does this need to exist and have different conventions
+ // than Bits?
def & (other: Bool): Bool = binop(Bool(), BitAndOp, other)
def | (other: Bool): Bool = binop(Bool(), BitOrOp, other)
def ^ (other: Bool): Bool = binop(Bool(), BitXorOp, other)
+ /** Outputs the logical OR of two Bools.
+ */
def || (that: Bool): Bool = this | that
+
+ /** Outputs the logical AND of two Bools.
+ */
def && (that: Bool): Bool = this & that
}
object Bool {
+ /** Creates an empty Bool.
+ */
def apply(dir: Direction = NO_DIR): Bool = new Bool(dir)
+
+ /** Creates Bool literal.
+ */
def apply(x: Boolean): Bool = new Bool(NO_DIR, Some(ULit(if (x) 1 else 0, Width(1))))
}
object Mux {
- /** Create a Mux
- * @param cond a condition to determine which value to choose
- * @param con the value chosen when cond is true or 1
- * @param alt the value chosen when cond is false or 0
+ /** Creates a mux, whose output is one of the inputs depending on the
+ * value of the condition.
+ *
+ * @param cond condition determining the input to choose
+ * @param con the value chosen when `cond` is true
+ * @param alt the value chosen when `cond` is false
* @example
- * {{{ val muxOut = Mux(data_in === UInt(3), UInt(3, 4), UInt(0, 4)) }}} */
+ * {{{
+ * val muxOut = Mux(data_in === UInt(3), UInt(3, 4), UInt(0, 4))
+ * }}}
+ */
def apply[T <: Data](cond: Bool, con: T, alt: T): T = (con, alt) match {
// Handle Mux(cond, UInt, Bool) carefully so that the concrete type is UInt
case (c: Bool, a: Bool) => doMux(cond, c, a).asInstanceOf[T]
@@ -681,6 +1064,8 @@ object Mux {
}
}
+// REVIEW TODO: Should the FIRRTL emission be part of Bits, with a separate
+// Cat in stdlib that can do a reduction among multiple elements?
object Cat {
/** Combine data elements together
* @param a Data to combine with
@@ -688,6 +1073,7 @@ object Cat {
* @return A UInt which is all of the bits combined together
*/
def apply[T <: Bits](a: T, r: T*): UInt = apply(a :: r.toList)
+
/** Combine data elements together
* @param r any number of other Data elements to be combined in order
* @return A UInt which is all of the bits combined together
@@ -706,9 +1092,11 @@ object Cat {
object Bundle {
private val keywords =
HashSet[String]("flip", "asInput", "asOutput", "cloneType", "toBits")
+
def apply[T <: Bundle](b: => T)(implicit p: Parameters): T = {
Builder.paramsScope(p.push){ b }
}
+
//TODO @deprecated("Use Chisel.paramsScope object","08-01-2015")
def apply[T <: Bundle](b: => T, f: PartialFunction[Any,Any]): T = {
val q = Builder.getParams.alterPartial(f)
@@ -716,35 +1104,49 @@ object Bundle {
}
}
-/** Define a collection of datum of different types into a single coherent
- whole.
+/** Base class for data types defined as a bundle of other data types.
+ *
+ * Usage: extend this class (either as an anonymous or named class) and define
+ * members variables of [[Data]] subtypes to be elements in the Bundle.
*/
class Bundle extends Aggregate(NO_DIR) {
private val _namespace = Builder.globalNamespace.child
- /** Connect all elements contained in this to node 'that'
- * @note The elements are checked for compatibility based on their name
- * If elements are in src that are not in this Bundle no warning will be produced
+ // REVIEW TODO: perhaps deprecate to match FIRRTL semantics? Also needs
+ // strong connect operator.
+ /** Connect elements in this Bundle to elements in `that` on a best-effort
+ * (weak) basis, matching by type, orientation, and name.
+ *
+ * @note unconnected elements will NOT generate errors or warnings
+ *
* @example
- * {{{ // pass through all wires in this modules io to the sub module which have the same name
- * // Note: ignores any extra defined in io
- * mySubModule.io <> io }}}
+ * {{{
+ * // Pass through wires in this module's io to those mySubModule's io,
+ * // matching by type, orientation, and name, and ignoring extra wires.
+ * mySubModule.io <> io
+ * }}}
*/
override def <> (that: Data): Unit = that match {
case _: Bundle => this bulkConnect that
case _ => this badConnect that
}
+ // REVIEW TODO: should there be different semantics for this? Or just ban it?
override def := (that: Data): Unit = this <> that
lazy val elements: ListMap[String, Data] = ListMap(namedElts:_*)
+ /** Returns a best guess at whether a field in this Bundle is a user-defined
+ * Bundle element.
+ */
private def isBundleField(m: java.lang.reflect.Method) =
m.getParameterTypes.isEmpty &&
!java.lang.reflect.Modifier.isStatic(m.getModifiers) &&
classOf[Data].isAssignableFrom(m.getReturnType) &&
!(Bundle.keywords contains m.getName) && !(m.getName contains '$')
+ /** Returns a list of elements in this Bundle.
+ */
private[Chisel] lazy val namedElts = {
val nameMap = LinkedHashMap[String, Data]()
val seen = HashSet[Data]()
@@ -793,10 +1195,15 @@ class Bundle extends Aggregate(NO_DIR) {
}
object Module {
- /** The wrapper method for all instantiations of modules
- * @param m The module newly created
+ // TODO: update documentation when parameters gets removed from core Chisel
+ // and this gets simplified.
+ /** A wrapper method that all Module instantiations must be wrapped in
+ * (necessary to help Chisel track internal state).
+ *
+ * @param m the Module being created
* @param p Parameters passed down implicitly from that it is created in
- * @return A properly wrapped module that has been added to the Driver
+ *
+ * @return the input module `m`
*/
def apply[T <: Module](bc: => T)(implicit currParams: Parameters = Builder.getParams.push): T = {
paramsScope(currParams) {
@@ -815,12 +1222,19 @@ object Module {
m
}.connectImplicitIOs()
}
+
//TODO @deprecated("Use Chisel.paramsScope object","08-01-2015")
def apply[T <: Module](m: => T, f: PartialFunction[Any,Any]): T = {
apply(m)(Builder.getParams.alterPartial(f))
}
}
+/** Abstract base class for Modules, which behave much like Verilog modules.
+ * These may contain both logic and state which are written in the Module
+ * body (constructor).
+ *
+ * @note Module instantiations must be wrapped in a Module() call.
+ */
abstract class Module(_clock: Clock = null, _reset: Bool = null) extends HasId {
private val _namespace = Builder.globalNamespace.child
private[Chisel] val _commands = ArrayBuffer[Command]()
@@ -830,7 +1244,9 @@ abstract class Module(_clock: Clock = null, _reset: Bool = null) extends HasId {
/** Name of the instance. */
val name = Builder.globalNamespace.name(getClass.getName.split('.').last)
- /** the I/O for this module */
+ /** IO for this Module. At the Scala level (pre-FIRRTL transformations),
+ * connections in and out of a Module may only go through `io` elements.
+ */
def io: Bundle
val clock = Clock(INPUT)
val reset = Bool(INPUT)
@@ -880,45 +1296,59 @@ abstract class Module(_clock: Clock = null, _reset: Bool = null) extends HasId {
def printf(message: String, args: Bits*): Unit = {}
}
-/** This class allows the connection to modules defined outside of chisel.
+/** Defines a black box, which is a module that can be referenced from within
+ * Chisel, but is not defined in the emitted Verilog. Useful for connecting
+ * to RTL modules defined outside Chisel.
+ *
* @example
- * {{{ class DSP48E1 extends BlackBox {
- * val io = new Bundle // Create I/O with same as DSP
- * val dspParams = new VerilogParameters // Create Parameters to be specified
- * setVerilogParams(dspParams)
- * // Implement functionality of DSP to allow simulation verification
- * } }}}
+ * {{{
+ * class DSP48E1 extends BlackBox {
+ * val io = new Bundle // Create I/O with same as DSP
+ * val dspParams = new VerilogParameters // Create Parameters to be specified
+ * setVerilogParams(dspParams)
+ * // Implement functionality of DSP to allow simulation verification
+ * }
+ * }}}
*/
// TODO: actually implement BlackBox (this hack just allows them to compile)
+// REVIEW TODO: make Verilog parameters part of the constructor interface?
abstract class BlackBox(_clock: Clock = null, _reset: Bool = null) extends Module(_clock = _clock, _reset = _reset) {
def setVerilogParameters(s: String): Unit = {}
}
-/** An object to create conditional logic.
- * @example
- * {{{
- * when ( myData === UInt(3) ) {
- * ... // Some logic
- * } .elsewhen ( myData === UInt(1) ) {
- * ... // Some logic
- * } .otherwise {
- * ... // Some logic
- * } }}}
- */
object when {
- /** @param cond condition to execute upon
- * @param block a section of logic to enable if cond is true */
+ /** Create a `when` condition block, where whether a block of logic is
+ * executed or not depends on the conditional.
+ *
+ * @param cond condition to execute upon
+ * @param block logic that runs only if `cond` is true
+ *
+ * @example
+ * {{{
+ * when ( myData === UInt(3) ) {
+ * // Some logic to run when myData equals 3.
+ * } .elsewhen ( myData === UInt(1) ) {
+ * // Some logic to run when myData equals 1.
+ * } .otherwise {
+ * // Some logic to run when myData is neither 3 nor 1.
+ * }
+ * }}}
+ */
def apply(cond: => Bool)(block: => Unit): WhenContext = {
new WhenContext(cond)(block)
}
}
class WhenContext(cond: => Bool)(block: => Unit) {
- /** execute block when alternative cond is true */
+ /** This block of logic gets executed if above conditions have been false
+ * and this condition is true.
+ */
def elsewhen (cond: => Bool)(block: => Unit): WhenContext =
doOtherwise(when(cond)(block))
- /** execute block by default */
+ /** This block of logic gets executed only if the above conditions were all
+ * false. No additional logic blocks may be appended past the `otherwise`.
+ */
def otherwise(block: => Unit): Unit =
doOtherwise(block)
@@ -934,6 +1364,8 @@ class WhenContext(cond: => Bool)(block: => Unit) {
}
}
+// TODO: check with FIRRTL specs, how much official implementation flexibility
+// is there?
/** A source of garbage data, used to initialize Wires to a don't-care value. */
private object Poison extends Command {
def apply[T <: Data](t: T): T =