From 4b51975ec7a543e165660d654fa84eaa9b9b3b3e Mon Sep 17 00:00:00 2001 From: ducky Date: Mon, 26 Oct 2015 18:08:47 -0700 Subject: Break Core.scala into bite-sized pieces --- src/main/scala/Chisel/Aggregate.scala | 330 ++++++++ src/main/scala/Chisel/BitPat.scala | 72 ++ src/main/scala/Chisel/Bits.scala | 539 +++++++++++++ src/main/scala/Chisel/BlackBox.scala | 23 + src/main/scala/Chisel/Cat.scala | 31 + src/main/scala/Chisel/Core.scala | 1410 --------------------------------- src/main/scala/Chisel/Data.scala | 141 ++++ src/main/scala/Chisel/Mem.scala | 99 +++ src/main/scala/Chisel/Module.scala | 108 +++ src/main/scala/Chisel/Reg.scala | 51 ++ src/main/scala/Chisel/Tester.scala | 39 + src/main/scala/Chisel/When.scala | 52 ++ 12 files changed, 1485 insertions(+), 1410 deletions(-) create mode 100644 src/main/scala/Chisel/Aggregate.scala create mode 100644 src/main/scala/Chisel/BitPat.scala create mode 100644 src/main/scala/Chisel/Bits.scala create mode 100644 src/main/scala/Chisel/BlackBox.scala create mode 100644 src/main/scala/Chisel/Cat.scala delete mode 100644 src/main/scala/Chisel/Core.scala create mode 100644 src/main/scala/Chisel/Data.scala create mode 100644 src/main/scala/Chisel/Mem.scala create mode 100644 src/main/scala/Chisel/Module.scala create mode 100644 src/main/scala/Chisel/Reg.scala create mode 100644 src/main/scala/Chisel/Tester.scala create mode 100644 src/main/scala/Chisel/When.scala (limited to 'src') diff --git a/src/main/scala/Chisel/Aggregate.scala b/src/main/scala/Chisel/Aggregate.scala new file mode 100644 index 00000000..962c2fd1 --- /dev/null +++ b/src/main/scala/Chisel/Aggregate.scala @@ -0,0 +1,330 @@ +// See LICENSE for license details. + +package Chisel +import scala.collection.immutable.ListMap +import scala.collection.mutable.{ArrayBuffer, HashSet, LinkedHashMap} +import Builder.pushCommand + +/** 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(_ + _) +} + +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("Vec argument order should be size, t; this will be removed by the official release", "chisel3") + def apply[T <: Data](gen: T, n: Int): Vec[T] = new Vec(gen.cloneType, n) + + /** 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) + pushCommand(DefWire(vec)) + for ((v, e) <- vec zip elts) + v := e + vec + } + + /** 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) + + /** 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] = + apply((0 until n).map(i => gen(i))) + + /** 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)) +} + +/** 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 { + case _: Vec[_] => this bulkConnect that + case _ => this badConnect that + } + + 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 + case _ => this badConnect that + } + + def := (that: Seq[T]): Unit = { + // REVIEW TODO: standardize as above + require(this.length == that.length) + for ((a, b) <- this zip that) + a := b + } + + 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 = + Vec(gen, length).asInstanceOf[this.type] + + private val t = gen + private[Chisel] def toType: String = s"${t.toType}[$length]" + private[Chisel] lazy val flatten: IndexedSeq[Bits] = + (0 until length).flatMap(i => this.apply(i).flatten) + + for ((elt, i) <- self zipWithIndex) + 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 + + /** 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? +} + +/** 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 + + // 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 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]() + for (m <- getClass.getMethods.sortWith(_.getName < _.getName); if isBundleField(m)) { + m.invoke(this) match { + case d: Data => + if (nameMap contains m.getName) { + require(nameMap(m.getName) eq d) + } else if (!seen(d)) { + nameMap(m.getName) = d; seen += d + } + case _ => + } + } + ArrayBuffer(nameMap.toSeq:_*) sortWith {case ((an, a), (bn, b)) => (a._id > b._id) || ((a eq b) && (an > bn))} + } + private[Chisel] def toType = { + def eltPort(elt: Data): String = { + val flipStr = if (elt.isFlip) "flip " else "" + s"${flipStr}${elt.getRef.name} : ${elt.toType}" + } + s"{${namedElts.reverse.map(e => eltPort(e._2)).mkString(", ")}}" + } + private[Chisel] lazy val flatten = namedElts.flatMap(_._2.flatten) + private[Chisel] def addElt(name: String, elt: Data): Unit = + namedElts += name -> elt + private[Chisel] override def _onModuleClose: Unit = + for ((name, elt) <- namedElts) { elt.setRef(this, _namespace.name(name)) } + + override def cloneType : this.type = { + // If the user did not provide a cloneType method, try invoking one of + // the following constructors, not all of which necessarily exist: + // - A zero-parameter constructor + // - A one-paramater constructor, with null as the argument + // - A one-parameter constructor for a nested Bundle, with the enclosing + // parent Module as the argument + val constructor = this.getClass.getConstructors.head + try { + val args = Seq.fill(constructor.getParameterTypes.size)(null) + constructor.newInstance(args:_*).asInstanceOf[this.type] + } catch { + case e: java.lang.reflect.InvocationTargetException if e.getCause.isInstanceOf[java.lang.NullPointerException] => + try { + constructor.newInstance(_parent.get).asInstanceOf[this.type] + } catch { + case _: java.lang.reflect.InvocationTargetException => + Builder.error(s"Parameterized Bundle ${this.getClass} needs cloneType method. You are probably using " + + "an anonymous Bundle object that captures external state and hence is un-cloneTypeable") + this + } + case _: java.lang.reflect.InvocationTargetException | _: java.lang.IllegalArgumentException => + Builder.error(s"Parameterized Bundle ${this.getClass} needs cloneType method") + this + } + } +} + +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) + apply(b)(q) + } +} diff --git a/src/main/scala/Chisel/BitPat.scala b/src/main/scala/Chisel/BitPat.scala new file mode 100644 index 00000000..1f0182e4 --- /dev/null +++ b/src/main/scala/Chisel/BitPat.scala @@ -0,0 +1,72 @@ +// See LICENSE for license details. + +package Chisel + +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) = { + // 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) { + if (d != '_') { + if (!"01?".contains(d)) Builder.error({"Literal: " + x + " contains illegal character: " + d}) + mask = (mask << 1) + (if (d == '?') 0 else 1) + bits = (bits << 1) + (if (d == '1') 1 else 0) + } + } + (bits, mask, x.length - 1) + } + + /** 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) + } + + /** 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) + 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)) + } +} + +// 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) +} diff --git a/src/main/scala/Chisel/Bits.scala b/src/main/scala/Chisel/Bits.scala new file mode 100644 index 00000000..0c8c12f8 --- /dev/null +++ b/src/main/scala/Chisel/Bits.scala @@ -0,0 +1,539 @@ +// See LICENSE for license details. + +package Chisel +import Builder.pushOp +import PrimOp._ + +/** 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) +} + +/** 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 + + /** 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) + + /** 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 = + pushOp(DefPrim(dest, op, this.ref)) + private[Chisel] def binop[T <: Data](dest: T, op: PrimOp, other: BigInt): T = + pushOp(DefPrim(dest, op, this.ref, ILit(other))) + private[Chisel] def binop[T <: Data](dest: T, op: PrimOp, other: Bits): T = + pushOp(DefPrim(dest, op, this.ref, other.ref)) + private[Chisel] def compop(op: PrimOp, other: Bits): Bool = + pushOp(DefPrim(Bool(), op, this.ref, other.ref)) + private[Chisel] def redop(op: PrimOp): Bool = + pushOp(DefPrim(Bool(), op, this.ref)) + + /** 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 + + /** 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 + final def toUInt(): UInt = asUInt + + def toBool(): Bool = width match { + case KnownWidth(1) => this(0) + case _ => throwException(s"can't covert UInt<$width> to Bool") + } + + // 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: UInt = asUInt + + override def fromBits(n: Bits): this.type = { + val res = Wire(this).asInstanceOf[this.type] + res := n + res + } +} + +// 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 + +// 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] + private[Chisel] def toType = s"UInt<$width>" + + def fromInt(value: BigInt): this.type = UInt(value).asInstanceOf[this.type] + def makeLit(value: BigInt): ULit = ULit(value, Width()) + + override def := (that: Data): Unit = that match { + case _: UInt => this connect that + case _ => this badConnect that + } + + // TODO: refactor to share documentation with Num or add independent scaladoc + def unary_- : UInt = UInt(0) - this + def unary_-% : UInt = UInt(0) -% this + def +& (other: UInt): UInt = binop(UInt((this.width max other.width) + 1), AddOp, other) + def + (other: UInt): UInt = this +% other + def +% (other: UInt): UInt = binop(UInt(this.width max other.width), AddModOp, other) + def -& (other: UInt): UInt = binop(UInt((this.width max other.width) + 1), SubOp, other) + def - (other: UInt): UInt = this -% other + def -% (other: UInt): UInt = binop(UInt(this.width max other.width), SubModOp, other) + def * (other: UInt): UInt = binop(UInt(this.width + other.width), TimesOp, other) + def * (other: SInt): SInt = other * this + def / (other: UInt): UInt = binop(UInt(this.width), DivideOp, other) + def % (other: UInt): UInt = binop(UInt(this.width), ModOp, other) + + def & (other: UInt): UInt = binop(UInt(this.width max other.width), BitAndOp, other) + 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: Bool = this != UInt(0) + def andR: Bool = ~this === UInt(0) + def xorR: Bool = redop(XorReduceOp) + + def < (other: UInt): Bool = compop(LessOp, other) + def > (other: UInt): Bool = compop(GreaterOp, other) + def <= (other: UInt): Bool = compop(LessEqOp, other) + def >= (other: UInt): Bool = compop(GreaterEqOp, other) + def != (other: UInt): Bool = compop(NotEqualOp, other) + 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) + def >> (other: Int): UInt = binop(UInt(this.width.shiftRight(other)), ShiftRightOp, other) + def >> (other: BigInt): UInt = this >> other.toInt + def >> (other: UInt): UInt = binop(UInt(this.width), DynamicShiftRightOp, other) + + def bitSet(off: UInt, dat: Bool): UInt = { + val bit = UInt(1, 1) << off + Mux(dat, this | bit, ~(~this | bit)) + } + + def === (that: BitPat): Bool = that === this + def != (that: BitPat): Bool = that != this + + // 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()) + /** Create a UInt type or port with fixed width. */ + def apply(dir: Direction = NO_DIR, width: Int): UInt = apply(dir, Width(width)) + /** Create a UInt port with inferred width. */ + def apply(dir: Direction): UInt = apply(dir, Width()) + + /** Create a UInt literal with inferred width. */ + def apply(value: BigInt): UInt = apply(value, Width()) + /** Create a UInt literal with fixed width. */ + def apply(value: BigInt, width: Int): UInt = apply(value, Width(width)) + /** Create a UInt literal with inferred width. */ + def apply(n: String): UInt = apply(parse(n), parsedWidth(n)) + /** Create a UInt literal with fixed width. */ + def apply(n: String, width: Int): UInt = apply(parse(n), width) + + /** Create a UInt type with specified width. */ + def apply(width: Width): UInt = apply(NO_DIR, width) + /** Create a UInt port with specified width. */ + def apply(dir: Direction, width: Width): UInt = new UInt(dir, width) + /** Create a UInt literal with specified width. */ + def apply(value: BigInt, width: Width): UInt = { + val lit = ULit(value, width) + new UInt(NO_DIR, lit.width, Some(lit)) + } + + private def parse(n: String) = { + val (base, num) = n.splitAt(1) + val radix = base match { + case "x" | "h" => 16 + case "d" => 10 + case "o" => 8 + case "b" => 2 + case _ => Builder.error(s"Invalid base $base"); 2 + } + BigInt(num, radix) + } + + private def parsedWidth(n: String) = + if (n(0) == 'b') { + Width(n.length-1) + } else if (n(0) == 'h') { + Width((n.length-1) * 4) + } else { + Width() + } +} + +object UInt extends UIntFactory + +sealed class SInt private (dir: Direction, width: Width, lit: Option[SLit] = None) + extends Bits(dir, width, lit) with Num[SInt] { + private[Chisel] override def cloneTypeWidth(w: Width): this.type = + new SInt(dir, w).asInstanceOf[this.type] + private[Chisel] def toType = s"SInt<$width>" + + override def := (that: Data): Unit = that match { + case _: SInt => this connect that + case _ => this badConnect that + } + + def fromInt(value: BigInt): this.type = SInt(value).asInstanceOf[this.type] + def makeLit(value: BigInt): SLit = SLit(value, Width()) + + def unary_- : SInt = SInt(0) - this + def unary_-% : SInt = SInt(0) -% this + /** add (width +1) operator */ + def +& (other: SInt): SInt = binop(SInt((this.width max other.width) + 1), AddOp, other) + /** add (default - no growth) operator */ + def + (other: SInt): SInt = this +% other + /** add (no growth) operator */ + def +% (other: SInt): SInt = binop(SInt(this.width max other.width), AddModOp, other) + /** subtract (width +1) operator */ + def -& (other: SInt): SInt = binop(SInt((this.width max other.width) + 1), SubOp, other) + /** subtract (default - no growth) operator */ + def - (other: SInt): SInt = this -% other + /** subtract (no growth) operator */ + def -% (other: SInt): SInt = binop(SInt(this.width max other.width), SubModOp, other) + def * (other: SInt): SInt = binop(SInt(this.width + other.width), TimesOp, other) + def * (other: UInt): SInt = binop(SInt(this.width + other.width), TimesOp, other) + def / (other: SInt): SInt = binop(SInt(this.width), DivideOp, other) + def % (other: SInt): SInt = binop(SInt(this.width), ModOp, other) + + def & (other: SInt): SInt = binop(SInt(this.width max other.width), BitAndOp, other) + def | (other: SInt): SInt = binop(SInt(this.width max other.width), BitOrOp, other) + def ^ (other: SInt): SInt = binop(SInt(this.width max other.width), BitXorOp, other) + + def < (other: SInt): Bool = compop(LessOp, other) + def > (other: SInt): Bool = compop(GreaterOp, other) + def <= (other: SInt): Bool = compop(LessEqOp, other) + def >= (other: SInt): Bool = compop(GreaterEqOp, other) + def != (other: SInt): Bool = compop(NotEqualOp, other) + def === (other: SInt): Bool = compop(EqualOp, other) + def abs(): UInt = Mux(this < SInt(0), (-this).toUInt, this.toUInt) + + def << (other: Int): SInt = binop(SInt(this.width + other), ShiftLeftOp, other) + def << (other: BigInt): SInt = this << other.toInt + def << (other: UInt): SInt = binop(SInt(this.width.dynamicShiftLeft(other.width)), DynamicShiftLeftOp, other) + def >> (other: Int): SInt = binop(SInt(this.width.shiftRight(other)), ShiftRightOp, other) + def >> (other: BigInt): SInt = this >> other.toInt + def >> (other: UInt): SInt = binop(SInt(this.width), DynamicShiftRightOp, other) + + def asUInt(): UInt = pushOp(DefPrim(UInt(this.width), AsUIntOp, ref)) + def asSInt(): SInt = this +} + +object SInt { + /** Create an SInt type with inferred width. */ + def apply(): SInt = apply(NO_DIR, Width()) + /** Create an SInt type or port with fixed width. */ + def apply(dir: Direction = NO_DIR, width: Int): SInt = apply(dir, Width(width)) + /** Create an SInt port with inferred width. */ + def apply(dir: Direction): SInt = apply(dir, Width()) + + /** Create an SInt literal with inferred width. */ + def apply(value: BigInt): SInt = apply(value, Width()) + /** Create an SInt literal with fixed width. */ + def apply(value: BigInt, width: Int): SInt = apply(value, Width(width)) + + /** Create an SInt type with specified width. */ + def apply(width: Width): SInt = new SInt(NO_DIR, width) + /** Create an SInt port with specified width. */ + def apply(dir: Direction, width: Width): SInt = new SInt(dir, width) + /** Create an SInt literal with specified width. */ + def apply(value: BigInt, width: Width): SInt = { + val lit = SLit(value, width) + new SInt(NO_DIR, lit.width, Some(lit)) + } +} + +// 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) + new Bool(dir).asInstanceOf[this.type] + } + + override def fromInt(value: BigInt): this.type = { + require(value == 0 || value == 1) + 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 { + /** 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)) + * }}} + */ + 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] + case (c: UInt, a: Bool) => doMux(cond, c, a << 0).asInstanceOf[T] + case (c: Bool, a: UInt) => doMux(cond, c << 0, a).asInstanceOf[T] + case (c: Bits, a: Bits) => doMux(cond, c, a).asInstanceOf[T] + // FIRRTL doesn't support Mux for aggregates, so use a when instead + case _ => doWhen(cond, con, alt) + } + + private def doMux[T <: Bits](cond: Bool, con: T, alt: T): T = { + require(con.getClass == alt.getClass, s"can't Mux between ${con.getClass} and ${alt.getClass}") + val d = alt.cloneTypeWidth(con.width max alt.width) + pushOp(DefPrim(d, MultiplexOp, cond.ref, con.ref, alt.ref)) + } + // This returns an lvalue, which it most definitely should not + private def doWhen[T <: Data](cond: Bool, con: T, alt: T): T = { + require(con.getClass == alt.getClass, s"can't Mux between ${con.getClass} and ${alt.getClass}") + val res = Wire(t = alt.cloneTypeWidth(con.width max alt.width), init = alt) + when (cond) { res := con } + res + } +} + diff --git a/src/main/scala/Chisel/BlackBox.scala b/src/main/scala/Chisel/BlackBox.scala new file mode 100644 index 00000000..dc223a17 --- /dev/null +++ b/src/main/scala/Chisel/BlackBox.scala @@ -0,0 +1,23 @@ +// See LICENSE for license details. + +package 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 + * } + * }}} + */ +// 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 = {} +} diff --git a/src/main/scala/Chisel/Cat.scala b/src/main/scala/Chisel/Cat.scala new file mode 100644 index 00000000..8075c11d --- /dev/null +++ b/src/main/scala/Chisel/Cat.scala @@ -0,0 +1,31 @@ +// See LICENSE for license details. + +package Chisel +import Builder.pushOp +import PrimOp._ + +// 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 + * @param r any number of other Data elements to be combined in order + * @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 + */ + def apply[T <: Bits](r: Seq[T]): UInt = { + if (r.tail.isEmpty) { + r.head.asUInt + } else { + val left = apply(r.slice(0, r.length/2)) + val right = apply(r.slice(r.length/2, r.length)) + val w = left.width + right.width + pushOp(DefPrim(UInt(w), ConcatOp, left.ref, right.ref)) + } + } +} diff --git a/src/main/scala/Chisel/Core.scala b/src/main/scala/Chisel/Core.scala deleted file mode 100644 index 945475fb..00000000 --- a/src/main/scala/Chisel/Core.scala +++ /dev/null @@ -1,1410 +0,0 @@ -// See LICENSE for license details. - -package Chisel -import scala.collection.immutable.ListMap -import scala.collection.mutable.{ArrayBuffer, HashSet, LinkedHashMap} -import Builder.pushCommand -import Builder.pushOp -import Builder.dynamicContext -import PrimOp._ - -sealed abstract class Direction(name: String) { - override def toString: String = name - def flip: Direction -} -object INPUT extends Direction("input") { override def flip: Direction = OUTPUT } -object OUTPUT extends Direction("output") { override def flip: Direction = INPUT } -object NO_DIR extends Direction("?") { override def flip: Direction = NO_DIR } - -// REVIEW TODO: Should this actually be part of the RTL API? RTL should be -// considered untouchable from a debugging standpoint? -object debug { // scalastyle:ignore object.name - // TODO: - def apply (arg: Data): Data = arg -} - -/** 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 - - // Sucks this is mutable state, but cloneType doesn't take a Direction arg - private var isFlipVar = dirArg == INPUT - private var dirVar = dirArg - private[Chisel] def isFlip = isFlipVar - - private def cloneWithDirection(newDir: Direction => Direction, - newFlip: Boolean => Boolean): this.type = { - val res = this.cloneType - res.isFlipVar = newFlip(res.isFlipVar) - for ((me, it) <- this.flatten zip res.flatten) - (it: Data).dirVar = newDir((me: Data).dirVar) - res - } - def asInput: this.type = cloneWithDirection(_ => INPUT, _ => true) - def asOutput: this.type = cloneWithDirection(_ => OUTPUT, _ => false) - def flip(): this.type = cloneWithDirection(_.flip, !_) - - private[Chisel] def badConnect(that: Data): Unit = - throwException(s"cannot connect ${this} and ${that}") - private[Chisel] def connect(that: Data): Unit = - pushCommand(Connect(this.lref, that.ref)) - private[Chisel] def bulkConnect(that: Data): Unit = - pushCommand(BulkConnect(this.lref, that.lref)) - private[Chisel] def lref: Node = Node(this) - private[Chisel] def ref: Arg = if (isLit) litArg.get else lref - 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 - def litArg(): Option[LitArg] = None - def litValue(): BigInt = litArg.get.num - def isLit(): Boolean = litArg.isDefined - - def width: Width - final def getWidth: Int = 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) { - x := n(i + x.getWidth-1, i) - i += x.getWidth - } - 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) -} - -object Wire { - def apply[T <: Data](t: T = null, init: T = null): T = { - val x = Reg.makeType(t, null.asInstanceOf[T], init) - pushCommand(DefWire(x)) - if (init != null) { - x := init - } else { - x.flatten.foreach(e => e := e.fromInt(0)) - } - x - } -} - -object Reg { - private[Chisel] def makeType[T <: Data](t: T = null, next: T = null, init: T = null): T = { - if (t ne null) { - t.cloneType - } else if (next ne null) { - next.cloneTypeWidth(Width()) - } else if (init ne null) { - init.litArg match { - // For e.g. Reg(init=UInt(0, k)), fix the Reg's width to k - case Some(lit) if lit.forcedWidth => init.cloneType - case _ => init.cloneTypeWidth(Width()) - } - } 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) { - pushCommand(ConnectInit(x.lref, init.ref)) - } - if (next != null) { - 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]) -} - -object Mem { - @deprecated("Mem argument order should be size, t; this will be removed by the official release", "chisel3") - 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) - pushCommand(DefMemory(mem, mt, size, Node(mt._parent.get.clock))) // TODO multi-clock - 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("SeqMem argument order should be size, t; this will be removed by the official release", "chisel3") - 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) - pushCommand(DefSeqMemory(mem, mt, size, Node(mt._parent.get.clock))) // TODO multi-clock - mem - } -} - -/** 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("Vec argument order should be size, t; this will be removed by the official release", "chisel3") - def apply[T <: Data](gen: T, n: Int): Vec[T] = new Vec(gen.cloneType, n) - - /** 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) - pushCommand(DefWire(vec)) - for ((v, e) <- vec zip elts) - v := e - vec - } - - /** 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) - - /** 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] = - apply((0 until n).map(i => gen(i))) - - /** 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 { - case _: Vec[_] => this bulkConnect that - case _ => this badConnect that - } - - 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 - case _ => this badConnect that - } - - def := (that: Seq[T]): Unit = { - // REVIEW TODO: standardize as above - require(this.length == that.length) - for ((a, b) <- this zip that) - a := b - } - - 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 = - Vec(gen, length).asInstanceOf[this.type] - - private val t = gen - private[Chisel] def toType: String = s"${t.toType}[$length]" - private[Chisel] lazy val flatten: IndexedSeq[Bits] = - (0 until length).flatMap(i => this.apply(i).flatten) - - for ((elt, i) <- self zipWithIndex) - 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 - - /** 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? -} - -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) = { - // 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) { - if (d != '_') { - if (!"01?".contains(d)) Builder.error({"Literal: " + x + " contains illegal character: " + d}) - mask = (mask << 1) + (if (d == '?') 0 else 1) - bits = (bits << 1) + (if (d == '1') 1 else 0) - } - } - (bits, mask, x.length - 1) - } - - /** 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) - } - - /** 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) - 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)) - } -} - -// 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) -} - -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() - private[Chisel] def cloneTypeWidth(width: Width): this.type = cloneType - private[Chisel] def toType = "Clock" - - override def := (that: Data): Unit = that match { - case _: Clock => this connect that - case _ => this badConnect that - } -} - -/** 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 - - /** 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) - - /** 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 = - pushOp(DefPrim(dest, op, this.ref)) - private[Chisel] def binop[T <: Data](dest: T, op: PrimOp, other: BigInt): T = - pushOp(DefPrim(dest, op, this.ref, ILit(other))) - private[Chisel] def binop[T <: Data](dest: T, op: PrimOp, other: Bits): T = - pushOp(DefPrim(dest, op, this.ref, other.ref)) - private[Chisel] def compop(op: PrimOp, other: Bits): Bool = - pushOp(DefPrim(Bool(), op, this.ref, other.ref)) - private[Chisel] def redop(op: PrimOp): Bool = - pushOp(DefPrim(Bool(), op, this.ref)) - - /** 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 - - /** 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 - final def toUInt(): UInt = asUInt - - def toBool(): Bool = width match { - case KnownWidth(1) => this(0) - case _ => throwException(s"can't covert UInt<$width> to Bool") - } - - // 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: UInt = asUInt - - override def fromBits(n: Bits): this.type = { - val res = Wire(this).asInstanceOf[this.type] - res := n - res - } -} - -// 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] - private[Chisel] def toType = s"UInt<$width>" - - def fromInt(value: BigInt): this.type = UInt(value).asInstanceOf[this.type] - def makeLit(value: BigInt): ULit = ULit(value, Width()) - - override def := (that: Data): Unit = that match { - case _: UInt => this connect that - case _ => this badConnect that - } - - // TODO: refactor to share documentation with Num or add independent scaladoc - def unary_- : UInt = UInt(0) - this - def unary_-% : UInt = UInt(0) -% this - def +& (other: UInt): UInt = binop(UInt((this.width max other.width) + 1), AddOp, other) - def + (other: UInt): UInt = this +% other - def +% (other: UInt): UInt = binop(UInt(this.width max other.width), AddModOp, other) - def -& (other: UInt): UInt = binop(UInt((this.width max other.width) + 1), SubOp, other) - def - (other: UInt): UInt = this -% other - def -% (other: UInt): UInt = binop(UInt(this.width max other.width), SubModOp, other) - def * (other: UInt): UInt = binop(UInt(this.width + other.width), TimesOp, other) - def * (other: SInt): SInt = other * this - def / (other: UInt): UInt = binop(UInt(this.width), DivideOp, other) - def % (other: UInt): UInt = binop(UInt(this.width), ModOp, other) - - def & (other: UInt): UInt = binop(UInt(this.width max other.width), BitAndOp, other) - 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: Bool = this != UInt(0) - def andR: Bool = ~this === UInt(0) - def xorR: Bool = redop(XorReduceOp) - - def < (other: UInt): Bool = compop(LessOp, other) - def > (other: UInt): Bool = compop(GreaterOp, other) - def <= (other: UInt): Bool = compop(LessEqOp, other) - def >= (other: UInt): Bool = compop(GreaterEqOp, other) - def != (other: UInt): Bool = compop(NotEqualOp, other) - 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) - def >> (other: Int): UInt = binop(UInt(this.width.shiftRight(other)), ShiftRightOp, other) - def >> (other: BigInt): UInt = this >> other.toInt - def >> (other: UInt): UInt = binop(UInt(this.width), DynamicShiftRightOp, other) - - def bitSet(off: UInt, dat: Bool): UInt = { - val bit = UInt(1, 1) << off - Mux(dat, this | bit, ~(~this | bit)) - } - - def === (that: BitPat): Bool = that === this - def != (that: BitPat): Bool = that != this - - // 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()) - /** Create a UInt type or port with fixed width. */ - def apply(dir: Direction = NO_DIR, width: Int): UInt = apply(dir, Width(width)) - /** Create a UInt port with inferred width. */ - def apply(dir: Direction): UInt = apply(dir, Width()) - - /** Create a UInt literal with inferred width. */ - def apply(value: BigInt): UInt = apply(value, Width()) - /** Create a UInt literal with fixed width. */ - def apply(value: BigInt, width: Int): UInt = apply(value, Width(width)) - /** Create a UInt literal with inferred width. */ - def apply(n: String): UInt = apply(parse(n), parsedWidth(n)) - /** Create a UInt literal with fixed width. */ - def apply(n: String, width: Int): UInt = apply(parse(n), width) - - /** Create a UInt type with specified width. */ - def apply(width: Width): UInt = apply(NO_DIR, width) - /** Create a UInt port with specified width. */ - def apply(dir: Direction, width: Width): UInt = new UInt(dir, width) - /** Create a UInt literal with specified width. */ - def apply(value: BigInt, width: Width): UInt = { - val lit = ULit(value, width) - new UInt(NO_DIR, lit.width, Some(lit)) - } - - private def parse(n: String) = { - val (base, num) = n.splitAt(1) - val radix = base match { - case "x" | "h" => 16 - case "d" => 10 - case "o" => 8 - case "b" => 2 - case _ => Builder.error(s"Invalid base $base"); 2 - } - BigInt(num, radix) - } - - private def parsedWidth(n: String) = - if (n(0) == 'b') { - Width(n.length-1) - } else if (n(0) == 'h') { - Width((n.length-1) * 4) - } else { - Width() - } -} - -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 - -sealed class SInt private (dir: Direction, width: Width, lit: Option[SLit] = None) - extends Bits(dir, width, lit) with Num[SInt] { - private[Chisel] override def cloneTypeWidth(w: Width): this.type = - new SInt(dir, w).asInstanceOf[this.type] - private[Chisel] def toType = s"SInt<$width>" - - override def := (that: Data): Unit = that match { - case _: SInt => this connect that - case _ => this badConnect that - } - - def fromInt(value: BigInt): this.type = SInt(value).asInstanceOf[this.type] - def makeLit(value: BigInt): SLit = SLit(value, Width()) - - def unary_- : SInt = SInt(0) - this - def unary_-% : SInt = SInt(0) -% this - /** add (width +1) operator */ - def +& (other: SInt): SInt = binop(SInt((this.width max other.width) + 1), AddOp, other) - /** add (default - no growth) operator */ - def + (other: SInt): SInt = this +% other - /** add (no growth) operator */ - def +% (other: SInt): SInt = binop(SInt(this.width max other.width), AddModOp, other) - /** subtract (width +1) operator */ - def -& (other: SInt): SInt = binop(SInt((this.width max other.width) + 1), SubOp, other) - /** subtract (default - no growth) operator */ - def - (other: SInt): SInt = this -% other - /** subtract (no growth) operator */ - def -% (other: SInt): SInt = binop(SInt(this.width max other.width), SubModOp, other) - def * (other: SInt): SInt = binop(SInt(this.width + other.width), TimesOp, other) - def * (other: UInt): SInt = binop(SInt(this.width + other.width), TimesOp, other) - def / (other: SInt): SInt = binop(SInt(this.width), DivideOp, other) - def % (other: SInt): SInt = binop(SInt(this.width), ModOp, other) - - def & (other: SInt): SInt = binop(SInt(this.width max other.width), BitAndOp, other) - def | (other: SInt): SInt = binop(SInt(this.width max other.width), BitOrOp, other) - def ^ (other: SInt): SInt = binop(SInt(this.width max other.width), BitXorOp, other) - - def < (other: SInt): Bool = compop(LessOp, other) - def > (other: SInt): Bool = compop(GreaterOp, other) - def <= (other: SInt): Bool = compop(LessEqOp, other) - def >= (other: SInt): Bool = compop(GreaterEqOp, other) - def != (other: SInt): Bool = compop(NotEqualOp, other) - def === (other: SInt): Bool = compop(EqualOp, other) - def abs(): UInt = Mux(this < SInt(0), (-this).toUInt, this.toUInt) - - def << (other: Int): SInt = binop(SInt(this.width + other), ShiftLeftOp, other) - def << (other: BigInt): SInt = this << other.toInt - def << (other: UInt): SInt = binop(SInt(this.width.dynamicShiftLeft(other.width)), DynamicShiftLeftOp, other) - def >> (other: Int): SInt = binop(SInt(this.width.shiftRight(other)), ShiftRightOp, other) - def >> (other: BigInt): SInt = this >> other.toInt - def >> (other: UInt): SInt = binop(SInt(this.width), DynamicShiftRightOp, other) - - def asUInt(): UInt = pushOp(DefPrim(UInt(this.width), AsUIntOp, ref)) - def asSInt(): SInt = this -} - -object SInt { - /** Create an SInt type with inferred width. */ - def apply(): SInt = apply(NO_DIR, Width()) - /** Create an SInt type or port with fixed width. */ - def apply(dir: Direction = NO_DIR, width: Int): SInt = apply(dir, Width(width)) - /** Create an SInt port with inferred width. */ - def apply(dir: Direction): SInt = apply(dir, Width()) - - /** Create an SInt literal with inferred width. */ - def apply(value: BigInt): SInt = apply(value, Width()) - /** Create an SInt literal with fixed width. */ - def apply(value: BigInt, width: Int): SInt = apply(value, Width(width)) - - /** Create an SInt type with specified width. */ - def apply(width: Width): SInt = new SInt(NO_DIR, width) - /** Create an SInt port with specified width. */ - def apply(dir: Direction, width: Width): SInt = new SInt(dir, width) - /** Create an SInt literal with specified width. */ - def apply(value: BigInt, width: Width): SInt = { - val lit = SLit(value, width) - new SInt(NO_DIR, lit.width, Some(lit)) - } -} - -// 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) - new Bool(dir).asInstanceOf[this.type] - } - - override def fromInt(value: BigInt): this.type = { - require(value == 0 || value == 1) - 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 { - /** 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)) - * }}} - */ - 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] - case (c: UInt, a: Bool) => doMux(cond, c, a << 0).asInstanceOf[T] - case (c: Bool, a: UInt) => doMux(cond, c << 0, a).asInstanceOf[T] - case (c: Bits, a: Bits) => doMux(cond, c, a).asInstanceOf[T] - // FIRRTL doesn't support Mux for aggregates, so use a when instead - case _ => doWhen(cond, con, alt) - } - - private def doMux[T <: Bits](cond: Bool, con: T, alt: T): T = { - require(con.getClass == alt.getClass, s"can't Mux between ${con.getClass} and ${alt.getClass}") - val d = alt.cloneTypeWidth(con.width max alt.width) - pushOp(DefPrim(d, MultiplexOp, cond.ref, con.ref, alt.ref)) - } - // This returns an lvalue, which it most definitely should not - private def doWhen[T <: Data](cond: Bool, con: T, alt: T): T = { - require(con.getClass == alt.getClass, s"can't Mux between ${con.getClass} and ${alt.getClass}") - val res = Wire(t = alt.cloneTypeWidth(con.width max alt.width), init = alt) - when (cond) { res := con } - res - } -} - -// 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 - * @param r any number of other Data elements to be combined in order - * @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 - */ - def apply[T <: Bits](r: Seq[T]): UInt = { - if (r.tail.isEmpty) { - r.head.asUInt - } else { - val left = apply(r.slice(0, r.length/2)) - val right = apply(r.slice(r.length/2, r.length)) - val w = left.width + right.width - pushOp(DefPrim(UInt(w), ConcatOp, left.ref, right.ref)) - } - } -} - -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) - apply(b)(q) - } -} - -/** 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 - - // 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 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]() - for (m <- getClass.getMethods.sortWith(_.getName < _.getName); if isBundleField(m)) { - m.invoke(this) match { - case d: Data => - if (nameMap contains m.getName) { - require(nameMap(m.getName) eq d) - } else if (!seen(d)) { - nameMap(m.getName) = d; seen += d - } - case _ => - } - } - ArrayBuffer(nameMap.toSeq:_*) sortWith {case ((an, a), (bn, b)) => (a._id > b._id) || ((a eq b) && (an > bn))} - } - private[Chisel] def toType = { - def eltPort(elt: Data): String = { - val flipStr = if (elt.isFlip) "flip " else "" - s"${flipStr}${elt.getRef.name} : ${elt.toType}" - } - s"{${namedElts.reverse.map(e => eltPort(e._2)).mkString(", ")}}" - } - private[Chisel] lazy val flatten = namedElts.flatMap(_._2.flatten) - private[Chisel] def addElt(name: String, elt: Data): Unit = - namedElts += name -> elt - private[Chisel] override def _onModuleClose: Unit = - for ((name, elt) <- namedElts) { elt.setRef(this, _namespace.name(name)) } - - override def cloneType : this.type = { - // If the user did not provide a cloneType method, try invoking one of - // the following constructors, not all of which necessarily exist: - // - A zero-parameter constructor - // - A one-paramater constructor, with null as the argument - // - A one-parameter constructor for a nested Bundle, with the enclosing - // parent Module as the argument - val constructor = this.getClass.getConstructors.head - try { - val args = Seq.fill(constructor.getParameterTypes.size)(null) - constructor.newInstance(args:_*).asInstanceOf[this.type] - } catch { - case e: java.lang.reflect.InvocationTargetException if e.getCause.isInstanceOf[java.lang.NullPointerException] => - try { - constructor.newInstance(_parent.get).asInstanceOf[this.type] - } catch { - case _: java.lang.reflect.InvocationTargetException => - Builder.error(s"Parameterized Bundle ${this.getClass} needs cloneType method. You are probably using " + - "an anonymous Bundle object that captures external state and hence is un-cloneTypeable") - this - } - case _: java.lang.reflect.InvocationTargetException | _: java.lang.IllegalArgumentException => - Builder.error(s"Parameterized Bundle ${this.getClass} needs cloneType method") - this - } - } -} - -object Module { - // 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 the input module `m` - */ - def apply[T <: Module](bc: => T)(implicit currParams: Parameters = Builder.getParams.push): T = { - paramsScope(currParams) { - val parent = dynamicContext.currentModule - val m = bc.setRefs() - // init module outputs - m._commands prependAll (for (p <- m.io.flatten; if p.dir == OUTPUT) - yield Connect(p.lref, p.fromInt(0).ref)) - dynamicContext.currentModule = parent - val ports = m.computePorts - Builder.components += Component(m, m.name, ports, m._commands) - pushCommand(DefInstance(m, ports)) - // init instance inputs - for (p <- m.io.flatten; if p.dir == INPUT) - p := p.fromInt(0) - 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]() - private[Chisel] val _ids = ArrayBuffer[HasId]() - dynamicContext.currentModule = Some(this) - - /** Name of the instance. */ - val name = Builder.globalNamespace.name(getClass.getName.split('.').last) - - /** 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) - - private[Chisel] def addId(d: HasId) { _ids += d } - private[Chisel] def ref = Builder.globalRefMap(this) - private[Chisel] def lref = ref - - private def ports = (clock, "clock") :: (reset, "reset") :: (io, "io") :: Nil - - private[Chisel] def computePorts = ports map { case (port, name) => - val bundleDir = if (port.isFlip) INPUT else OUTPUT - Port(port, if (port.dir == NO_DIR) bundleDir else port.dir) - } - - private def connectImplicitIOs(): this.type = _parent match { - case Some(p) => - clock := (if (_clock eq null) p.clock else _clock) - reset := (if (_reset eq null) p.reset else _reset) - this - case None => this - } - - private def makeImplicitIOs(): Unit = ports map { case (port, name) => - } - - private def setRefs(): this.type = { - for ((port, name) <- ports) - port.setRef(ModuleIO(this, _namespace.name(name))) - - val valNames = HashSet[String](getClass.getDeclaredFields.map(_.getName):_*) - def isPublicVal(m: java.lang.reflect.Method) = - m.getParameterTypes.isEmpty && valNames.contains(m.getName) - val methods = getClass.getMethods.sortWith(_.getName > _.getName) - for (m <- methods; if isPublicVal(m)) m.invoke(this) match { - case id: HasId => id.setRef(_namespace.name(m.getName)) - case _ => - } - - _ids.foreach(_.setRef(_namespace.name("T"))) - _ids.foreach(_._onModuleClose) - this - } - - // TODO: actually implement these - def assert(cond: Bool, msg: String): Unit = {} - def printf(message: String, args: Bits*): Unit = {} -} - -/** 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 - * } - * }}} - */ -// 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 = {} -} - -object when { // scalastyle:ignore object.name - /** 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) { - /** 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)) - - /** 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) - - pushCommand(WhenBegin(cond.ref)) - block - pushCommand(WhenEnd()) - - private def doOtherwise[T](block: => T): T = { - pushCommand(WhenElse()) - val res = block - pushCommand(WhenEnd()) - res - } -} - -// 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 = - pushCommand(DefPoison(t.cloneType)).id -} diff --git a/src/main/scala/Chisel/Data.scala b/src/main/scala/Chisel/Data.scala new file mode 100644 index 00000000..d30edda1 --- /dev/null +++ b/src/main/scala/Chisel/Data.scala @@ -0,0 +1,141 @@ +// See LICENSE for license details. + +package Chisel +import Builder.pushCommand + +sealed abstract class Direction(name: String) { + override def toString: String = name + def flip: Direction +} +object INPUT extends Direction("input") { override def flip: Direction = OUTPUT } +object OUTPUT extends Direction("output") { override def flip: Direction = INPUT } +object NO_DIR extends Direction("?") { override def flip: Direction = NO_DIR } + +// REVIEW TODO: Should this actually be part of the RTL API? RTL should be +// considered untouchable from a debugging standpoint? +object debug { // scalastyle:ignore object.name + // TODO: + def apply (arg: Data): Data = arg +} + +/** 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 + + // Sucks this is mutable state, but cloneType doesn't take a Direction arg + private var isFlipVar = dirArg == INPUT + private var dirVar = dirArg + private[Chisel] def isFlip = isFlipVar + + private def cloneWithDirection(newDir: Direction => Direction, + newFlip: Boolean => Boolean): this.type = { + val res = this.cloneType + res.isFlipVar = newFlip(res.isFlipVar) + for ((me, it) <- this.flatten zip res.flatten) + (it: Data).dirVar = newDir((me: Data).dirVar) + res + } + def asInput: this.type = cloneWithDirection(_ => INPUT, _ => true) + def asOutput: this.type = cloneWithDirection(_ => OUTPUT, _ => false) + def flip(): this.type = cloneWithDirection(_.flip, !_) + + private[Chisel] def badConnect(that: Data): Unit = + throwException(s"cannot connect ${this} and ${that}") + private[Chisel] def connect(that: Data): Unit = + pushCommand(Connect(this.lref, that.ref)) + private[Chisel] def bulkConnect(that: Data): Unit = + pushCommand(BulkConnect(this.lref, that.lref)) + private[Chisel] def lref: Node = Node(this) + private[Chisel] def ref: Arg = if (isLit) litArg.get else lref + 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 + def litArg(): Option[LitArg] = None + def litValue(): BigInt = litArg.get.num + def isLit(): Boolean = litArg.isDefined + + def width: Width + final def getWidth: Int = 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) { + x := n(i + x.getWidth-1, i) + i += x.getWidth + } + 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) +} + +object Wire { + def apply[T <: Data](t: T = null, init: T = null): T = { + val x = Reg.makeType(t, null.asInstanceOf[T], init) + pushCommand(DefWire(x)) + if (init != null) { + x := init + } else { + x.flatten.foreach(e => e := e.fromInt(0)) + } + x + } +} + +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() + private[Chisel] def cloneTypeWidth(width: Width): this.type = cloneType + private[Chisel] def toType = "Clock" + + override def := (that: Data): Unit = that match { + case _: Clock => this connect that + case _ => this badConnect that + } +} + +// 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 = + pushCommand(DefPoison(t.cloneType)).id +} diff --git a/src/main/scala/Chisel/Mem.scala b/src/main/scala/Chisel/Mem.scala new file mode 100644 index 00000000..87f717b6 --- /dev/null +++ b/src/main/scala/Chisel/Mem.scala @@ -0,0 +1,99 @@ +// See LICENSE for license details. + +package Chisel +import Builder.pushCommand + +object Mem { + @deprecated("Mem argument order should be size, t; this will be removed by the official release", "chisel3") + 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) + pushCommand(DefMemory(mem, mt, size, Node(mt._parent.get.clock))) // TODO multi-clock + 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("SeqMem argument order should be size, t; this will be removed by the official release", "chisel3") + 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) + pushCommand(DefSeqMemory(mem, mt, size, Node(mt._parent.get.clock))) // TODO multi-clock + mem + } +} + +/** 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))) +} diff --git a/src/main/scala/Chisel/Module.scala b/src/main/scala/Chisel/Module.scala new file mode 100644 index 00000000..1c1c02de --- /dev/null +++ b/src/main/scala/Chisel/Module.scala @@ -0,0 +1,108 @@ +// See LICENSE for license details. + +package Chisel +import scala.collection.mutable.{ArrayBuffer, HashSet} +import Builder.pushCommand +import Builder.dynamicContext + +object Module { + // 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 the input module `m` + */ + def apply[T <: Module](bc: => T)(implicit currParams: Parameters = Builder.getParams.push): T = { + paramsScope(currParams) { + val parent = dynamicContext.currentModule + val m = bc.setRefs() + // init module outputs + m._commands prependAll (for (p <- m.io.flatten; if p.dir == OUTPUT) + yield Connect(p.lref, p.fromInt(0).ref)) + dynamicContext.currentModule = parent + val ports = m.computePorts + Builder.components += Component(m, m.name, ports, m._commands) + pushCommand(DefInstance(m, ports)) + // init instance inputs + for (p <- m.io.flatten; if p.dir == INPUT) + p := p.fromInt(0) + 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]() + private[Chisel] val _ids = ArrayBuffer[HasId]() + dynamicContext.currentModule = Some(this) + + /** Name of the instance. */ + val name = Builder.globalNamespace.name(getClass.getName.split('.').last) + + /** 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) + + private[Chisel] def addId(d: HasId) { _ids += d } + private[Chisel] def ref = Builder.globalRefMap(this) + private[Chisel] def lref = ref + + private def ports = (clock, "clock") :: (reset, "reset") :: (io, "io") :: Nil + + private[Chisel] def computePorts = ports map { case (port, name) => + val bundleDir = if (port.isFlip) INPUT else OUTPUT + Port(port, if (port.dir == NO_DIR) bundleDir else port.dir) + } + + private def connectImplicitIOs(): this.type = _parent match { + case Some(p) => + clock := (if (_clock eq null) p.clock else _clock) + reset := (if (_reset eq null) p.reset else _reset) + this + case None => this + } + + private def makeImplicitIOs(): Unit = ports map { case (port, name) => + } + + private def setRefs(): this.type = { + for ((port, name) <- ports) + port.setRef(ModuleIO(this, _namespace.name(name))) + + val valNames = HashSet[String](getClass.getDeclaredFields.map(_.getName):_*) + def isPublicVal(m: java.lang.reflect.Method) = + m.getParameterTypes.isEmpty && valNames.contains(m.getName) + val methods = getClass.getMethods.sortWith(_.getName > _.getName) + for (m <- methods; if isPublicVal(m)) m.invoke(this) match { + case id: HasId => id.setRef(_namespace.name(m.getName)) + case _ => + } + + _ids.foreach(_.setRef(_namespace.name("T"))) + _ids.foreach(_._onModuleClose) + this + } + + // TODO: actually implement these + def assert(cond: Bool, msg: String): Unit = {} + def printf(message: String, args: Bits*): Unit = {} +} diff --git a/src/main/scala/Chisel/Reg.scala b/src/main/scala/Chisel/Reg.scala new file mode 100644 index 00000000..f870f544 --- /dev/null +++ b/src/main/scala/Chisel/Reg.scala @@ -0,0 +1,51 @@ +// See LICENSE for license details. + +package Chisel +import Builder.pushCommand + +object Reg { + private[Chisel] def makeType[T <: Data](t: T = null, next: T = null, init: T = null): T = { + if (t ne null) { + t.cloneType + } else if (next ne null) { + next.cloneTypeWidth(Width()) + } else if (init ne null) { + init.litArg match { + // For e.g. Reg(init=UInt(0, k)), fix the Reg's width to k + case Some(lit) if lit.forcedWidth => init.cloneType + case _ => init.cloneTypeWidth(Width()) + } + } 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) { + pushCommand(ConnectInit(x.lref, init.ref)) + } + if (next != null) { + 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]) +} diff --git a/src/main/scala/Chisel/Tester.scala b/src/main/scala/Chisel/Tester.scala new file mode 100644 index 00000000..d02af842 --- /dev/null +++ b/src/main/scala/Chisel/Tester.scala @@ -0,0 +1,39 @@ +// See LICENSE for license details. + +package Chisel + +import scala.util.Random + +class Tester[+T <: Module](c: T, isTrace: Boolean = true) { + def t: Int = 0 + var ok: Boolean = true // TODO: get rid of this + + def rnd: Random = new Random() + + def peek(data: Bits): BigInt = 0 + def poke(data: Bits, x: BigInt): Unit = {} + def expect(data: Bits, expected: BigInt): Boolean = true + def step(n: Int): Unit = {} + + // TODO: unify and disambiguate expect(...) + def expect(ok: Boolean, failureMsg: String): Boolean = true +} + +object chiselMainOld { + val wrapped = true + val unwrapped = false + + def apply[T <: Module](args: Array[String], gen: () => T): T = gen() + + def apply[T <: Module](args: Array[String], gen: () => T, ftester: T => Tester[T]): T = gen() + + // Assumes gen needs to be wrapped in Module() + def run[T <: Module] (args: Array[String], gen: () => T): T = gen() + + def run[T <: Module] (args: Array[String], gen: () => T, ftester: T => Tester[T]): T = gen() +} + +object chiselMainTest { + def apply[T <: Module](args: Array[String], gen: () => T)(tester: T => Tester[T]): T = + chiselMainOld(args, gen, tester) +} diff --git a/src/main/scala/Chisel/When.scala b/src/main/scala/Chisel/When.scala new file mode 100644 index 00000000..a051e52c --- /dev/null +++ b/src/main/scala/Chisel/When.scala @@ -0,0 +1,52 @@ +// See LICENSE for license details. + +package Chisel +import Builder.pushCommand + +object when { // scalastyle:ignore object.name + /** 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) { + /** 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)) + + /** 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) + + pushCommand(WhenBegin(cond.ref)) + block + pushCommand(WhenEnd()) + + private def doOtherwise[T](block: => T): T = { + pushCommand(WhenElse()) + val res = block + pushCommand(WhenEnd()) + res + } +} -- cgit v1.2.3