diff options
Diffstat (limited to 'src/main')
| -rw-r--r-- | src/main/scala/Backend.scala | 10 | ||||
| -rw-r--r-- | src/main/scala/Core.scala | 1154 | ||||
| -rw-r--r-- | src/main/scala/Driver.scala | 421 | ||||
| -rw-r--r-- | src/main/scala/Enum.scala | 44 | ||||
| -rw-r--r-- | src/main/scala/Error.scala | 167 | ||||
| -rw-r--r-- | src/main/scala/FP.scala | 319 | ||||
| -rw-r--r-- | src/main/scala/Literal.scala | 199 | ||||
| -rw-r--r-- | src/main/scala/Tester.scala | 389 | ||||
| -rw-r--r-- | src/main/scala/utils.scala | 253 |
9 files changed, 2956 insertions, 0 deletions
diff --git a/src/main/scala/Backend.scala b/src/main/scala/Backend.scala new file mode 100644 index 00000000..9b8bfb6d --- /dev/null +++ b/src/main/scala/Backend.scala @@ -0,0 +1,10 @@ +package Chisel +import Chisel._ + +class Backend; +class FloBackend extends Backend; +class CppBackend extends Backend; +class VerilogBackend extends Backend; +class FPGABackend extends Backend; +class DotBackend extends Backend; +class SysCBackend extends Backend; diff --git a/src/main/scala/Core.scala b/src/main/scala/Core.scala new file mode 100644 index 00000000..7725d242 --- /dev/null +++ b/src/main/scala/Core.scala @@ -0,0 +1,1154 @@ +package Chisel +import scala.collection.mutable.{ArrayBuffer, Stack, HashSet, HashMap} +import java.lang.reflect.Modifier._ +import scala.math.max + +class GenSym { + var counter = -1 + def next(name: String): String = { + counter += 1 + name + "_" + counter + } +} + +object Builder { + val components = new ArrayBuffer[Component]() + val genSym = new GenSym() + val scopes = new Stack[HashSet[String]]() + def scope = scopes.top + val switchKeyz = new Stack[Stack[Bits]]() + def switchKeys = switchKeyz.top + def pushScope = { + scopes.push(new HashSet[String]()) + switchKeyz.push(new Stack[Bits]()) + } + def popScope = { + scopes.pop() + switchKeyz.pop() + } + val modules = new HashMap[String,Module]() + def addModule(mod: Module) { + modules(mod.id) = mod + } + val modulez = new Stack[Module]() + def pushModule(mod: Module) { + modulez.push(mod) + } + def popModule() { + modulez.pop + } + val componentNames = new HashSet[String]() + def UniqueComponent(name: String, ports: Array[Port], body: Command) = { + val res = Component(if (componentNames.contains(name)) genSym.next(name) else name, ports, body) + componentNames += name + res + } + val commandz = new Stack[ArrayBuffer[Command]]() + def commands = commandz.top + def pushCommand(cmd: Command) = commands += cmd + def commandify(cmds: ArrayBuffer[Command]): Command = { + if (cmds.length == 0) + EmptyCommand() + else if (cmds.length == 1) + cmds(0) + else + Begin(cmds.toArray) + } + def pushCommands = + commandz.push(new ArrayBuffer[Command]()) + def popCommands: Command = { + val newCommands = commands + commandz.pop() + commandify(newCommands) + } + def collectCommands[T <: Module](f: => T): (Command, T) = { + pushCommands + val mod = f + // mod.setRefs + (popCommands, mod) + } + + private val refmap = new HashMap[String,Immediate]() + + def setRefForId(id: String, name: String, overwrite: Boolean = false) { + if (overwrite || !refmap.contains(id)) { + refmap(id) = Ref(name) + } + } + + def setFieldForId(parentid: String, id: String, name: String) { + refmap(id) = Field(Alias(parentid), name) + } + + def setIndexForId(parentid: String, id: String, index: Int) { + refmap(id) = Index(Alias(parentid), index) + } + + def getRefForId(id: String): Immediate = { + if (refmap.contains(id)) { + refmap(id) + } else { + val ref = Ref(genSym.next("T")) + refmap(id) = ref + ref + } + } + + def build[T <: Module](f: => T): (Circuit, T) = { + val (cmd, mod) = collectCommands(f) + (Circuit(components.toArray, components.last.name), mod) + } + +} + +import Builder._ + +/// CHISEL IR + +case class PrimOp(val name: String) { + override def toString = name +} + +object PrimOp { + val AddOp = PrimOp("add") + val AddModOp = PrimOp("add-wrap") + val SubOp = PrimOp("sub") + val SubModOp = PrimOp("sub-wrap") + val TimesOp = PrimOp("mul") + val DivideOp = PrimOp("div") + val ModOp = PrimOp("mod") + val ShiftLeftOp = PrimOp("shl") + val ShiftRightOp = PrimOp("shr") + val DynamicShiftLeftOp = PrimOp("dshl") + val DynamicShiftRightOp = PrimOp("dshr") + val AndOp = PrimOp("bit-and") + val OrOp = PrimOp("bit-or") + val BitAndOp = PrimOp("bit-and") + val BitOrOp = PrimOp("bit-or") + val BitXorOp = PrimOp("bit-xor") + val BitNotOp = PrimOp("bit-not") + val NotOp = PrimOp("bit-not") + val ConcatOp = PrimOp("cat") + val BitSelectOp = PrimOp("bit") + val BitsExtractOp = PrimOp("bits") + val LessOp = PrimOp("lt") + val LessEqOp = PrimOp("leq") + val GreaterOp = PrimOp("gt") + val GreaterEqOp = PrimOp("geq") + val EqualOp = PrimOp("eq") + val PatternEqualOp = PrimOp("pattern-equal") + val PadOp = PrimOp("Pad") + val NotEqualOp = PrimOp("neq") + val NegOp = PrimOp("neg") + val MultiplexOp = PrimOp("mux") + val AndReduceOp = PrimOp("and-reduce") + val OrReduceOp = PrimOp("or-reduce") + val XorReduceOp = PrimOp("xor-reduce") +} +import PrimOp._ + +abstract class Immediate { + def fullname: String + def name: String +} + +case class Alias(val id: String) extends Immediate { + def fullname = getRefForId(id).fullname + def name = getRefForId(id).name +} +case class Ref(val name: String) extends Immediate { + def fullname = name +} +case class Field(val imm: Immediate, val name: String) extends Immediate { + def fullname = { + val imm_fullname = imm.fullname + if (imm_fullname == "this") name else imm_fullname + "." + name + } +} +case class Index(val imm: Immediate, val value: Int) extends Immediate { + def name = "." + value + def fullname = { + imm.fullname + "." + value + } +} + +case class Port(val id: String, val dir: Direction, val kind: Type); + +abstract class Width; +case class UnknownWidth() extends Width; +case class IntWidth(val value: Int) extends Width; + +abstract class Type(val isFlip: Boolean); +case class UnknownType(flip: Boolean) extends Type(flip); +case class UIntType(val width: Width, flip: Boolean) extends Type(flip); +case class SIntType(val width: Width, flip: Boolean) extends Type(flip); +case class FloType(flip: Boolean) extends Type(flip); +case class DblType(flip: Boolean) extends Type(flip); +case class BundleType(val ports: Array[Port], flip: Boolean) extends Type(flip); +case class VectorType(val size: Int, val kind: Type, flip: Boolean) extends Type(flip); + +abstract class Command; +abstract class Definition extends Command { + def id: String + def name = getRefForId(id).name +} +case class DefUInt(val id: String, val value: BigInt, val width: Int) extends Definition; +case class DefSInt(val id: String, val value: BigInt, val width: Int) extends Definition; +case class DefFlo(val id: String, val value: Float) extends Definition; +case class DefDbl(val id: String, val value: Double) extends Definition; +case class DefMInt(val id: String, val value: String, val width: Int) extends Definition; +case class DefPrim(val id: String, val kind: Type, val op: PrimOp, val args: Array[Alias], val lits: Array[BigInt]) extends Definition; +case class DefPrimPad(val id: String, val kind: Type, val op: PrimOp, val args: Array[Alias], val lits: Array[BigInt]) extends Definition; +case class DefWire(val id: String, val kind: Type) extends Definition; +case class DefRegister(val id: String, val kind: Type) extends Definition; +case class DefMemory(val id: String, val kind: Type, val size: Int) extends Definition; +case class DefAccessor(val id: String, val source: Alias, val direction: Direction, val index: Alias) extends Definition; +case class DefInstance(val id: String, val module: String) extends Definition; +case class Conditionally(val pred: Alias, val conseq: Command, val alt: Command) extends Command; +case class Begin(val body: Array[Command]) extends Command(); +case class Connect(val loc: Alias, val exp: Alias) extends Command; +case class ConnectPad(val loc: Alias, val exp: Alias) extends Command; +case class ConnectInit(val loc: Alias, val exp: Alias) extends Command; +case class ConnectInitIndex(val loc: Alias, val index: Int, val exp: Alias) extends Command; +case class EmptyCommand() extends Command; + +case class Component(val name: String, val ports: Array[Port], val body: Command); +case class Circuit(val components: Array[Component], val main: String); + +object Commands { + val NoLits = Array[BigInt]() +} + +import Commands._ + +/// COMPONENTS + +sealed abstract class Direction(val name: String) { + override def toString = name +} +object INPUT extends Direction("input") +object OUTPUT extends Direction("output") +object NO_DIR extends Direction("?") + +object Direction { + def flipDirection(dir: Direction) = { + dir match { + case INPUT => OUTPUT + case OUTPUT => INPUT + case NO_DIR => NO_DIR + } + } +} +import Direction._ + +/// CHISEL FRONT-END + +abstract class Id { + val id = genSym.next("id") + var isDef_ = false + def defd: this.type = { + isDef_ = true + this + } + def isDef = isDef_ +} + +abstract class Data(dirArg: Direction) extends Id { + val mod = modulez.top + def toType: Type + var isFlipVar = dirArg == INPUT + def isFlip = isFlipVar + def dir: Direction = if (isFlip) INPUT else OUTPUT + def setDir(dir: Direction) { + isFlipVar = (dir == INPUT) + } + def asInput: this.type = { + setDir(INPUT) + this + } + def asOutput: this.type = { + setDir(OUTPUT) + this + } + def flip: this.type = { + isFlipVar = !isFlipVar + this + } + def :=(other: Data) = + pushCommand(Connect(this.ref, other.ref)) + def <>(other: Data) = + pushCommand(Connect(this.ref, other.ref)) + def cloneType: this.type + def cloneTypeWidth(width: Int): this.type + def ref: Alias = + Alias(id) + def name = getRefForId(id).name + def debugName = mod.debugName + "." + name + def getWidth: Int + def flatten: Array[Bits] + def fromBits(n: Bits): this.type = { + val res = this.cloneType + var i = 0 + val wire = Wire(res) + for (x <- wire.flatten.reverse) { + x := n(i + x.getWidth-1, i) + i += x.getWidth + } + res + } + def toBits: Bits = { + val elts = this.flatten + Cat(elts.head, elts.tail:_*) + } + def makeLit(value: BigInt, width: Int): this.type = { + val x = cloneType + x.fromBits(Bits(value, width)) + x + } + def toBool(): Bool = + chiselCast(this){Bool()} + + def toPort: Port = Port(id, dir, toType) + def collectElts: Unit + var isReg_ = false + def isReg = isReg_ +} + +object Wire { + def apply[T <: Data](t: T = null, init: T = null): T = { + val mType = if (t == null) init else t + if(mType == null) + throw new Exception("cannot infer type of Init.") + val x = mType.cloneType + // TODO: COME UP WITH MORE ROBUST WAY TO HANDLE THIS + x.collectElts + pushCommand(DefWire(x.defd.id, x.toType)) + if (init != null) + pushCommand(Connect(x.ref, init.ref)) + x + } +} + +object Reg { + def apply[T <: Data](t: T = null, next: T = null, init: T = null): T = { + var mType = t + if(mType == null) + mType = next + if(mType == null) + mType = init + if(mType == null) + throw new Exception("cannot infer type of Reg.") + val x = mType.cloneType + x.isReg_ = true + pushCommand(DefRegister(x.defd.id, x.toType)) + if (init != null) + pushCommand(ConnectInit(x.ref, init.ref)) + if (next != null) + pushCommand(ConnectPad(x.ref, next.ref)) + x + } + def apply[T <: Data](outType: T): T = Reg[T](outType, null.asInstanceOf[T], null.asInstanceOf[T]) +} + +object Mem { + def apply[T <: Data](t: T, size: Int): Mem[T] = { + val mt = t.cloneType + val mem = new Mem(mt, size) + pushCommand(DefMemory(mt.defd.id, mt.toType, size)) + mem + } +} + +class Mem[T <: Data](val t: T, val size: Int) /* with VecLike[T] */ { // TODO: VECLIKE + def apply(idx: Bits): T = { + val x = t.cloneType + pushCommand(DefAccessor(x.defd.id, Alias(t.id), NO_DIR, idx.ref)) + x + } +} + +object Vec { + def apply[T <: Data](gen: T, n: Int): Vec[T] = + new Vec((0 until n).map(i => gen.cloneType)) + def apply[T <: Data](elts: Iterable[T]): Vec[T] = { + val vec = new Vec[T](elts.map(e => elts.head.cloneType)) + vec.collectElts + val isDef = true || elts.head.isDef + if (vec.isReg) + throw new Exception("Vec of Reg Deprecated.") + if (isDef) { + pushCommand(DefWire(vec.defd.id, vec.toType)) + var i = 0 + for (elt <- elts) { + pushCommand(ConnectPad(vec(i).ref, elt.ref)) + i += 1 + } + } + vec + } + def apply[T <: Data](elt0: T, elts: T*): Vec[T] = + apply(elt0 +: elts.toSeq) + def tabulate[T <: Data](n: Int)(gen: (Int) => T): Vec[T] = + apply((0 until n).map(i => gen(i))) + def fill[T <: Data](n: Int)(gen: => T): Vec[T] = + Vec.tabulate(n){ i => gen } +} + +abstract class Aggregate(dirArg: Direction) extends Data(dirArg) { + def cloneTypeWidth(width: Int): this.type = cloneType +} + +class Vec[T <: Data](val elts: Iterable[T], dirArg: Direction = NO_DIR) extends Aggregate(dirArg) with VecLike[T] { + val elt0 = elts.head + val self = new ArrayBuffer[T]() + self ++= elts + override def isReg = elt0.isReg + override def isFlip = { + val isSubFlip = elt0.isFlip + if (isFlipVar) !isSubFlip else isSubFlip + } + + def apply(idx: UInt): T = { + val x = elt0.cloneType + pushCommand(DefAccessor(x.defd.id, Alias(id), NO_DIR, idx.ref)) + x + } + def apply(idx: Int): T = + self(idx) + def toPorts: Array[Port] = + self.map(d => d.toPort).toArray + def toType: Type = + VectorType(self.size, elt0.toType, isFlipVar) + override def cloneType: this.type = { + val v = Vec(elt0.cloneType, self.size).asInstanceOf[this.type] + v.collectElts + v + } + def inits (f: (Int, T, (Int, T, T) => Unit) => Unit) = { + var i = 0 + def doInit (index: Int, elt: T, init: T) = + pushCommand(ConnectInitIndex(elt.ref, index, init.ref)) + for (d <- self) { + f(i, d, doInit) + i += 1; + } + } + override def flatten: Array[Bits] = + self.map(_.flatten).reduce(_ ++ _) + override def getWidth: Int = + flatten.map(_.getWidth).reduce(_ + _) + + def collectElts: Unit = { + for (i <- 0 until self.size) { + val elt = self(i) + setIndexForId(id, elt.id, i) + elt.collectElts + } + } + + def length: Int = self.size +} + +trait VecLike[T <: Data] extends collection.IndexedSeq[T] { + // def read(idx: UInt): T + // def write(idx: UInt, data: T): Unit + def apply(idx: UInt): T + + def forall(p: T => Bool): Bool = (this map p).fold(Bool(true))(_&&_) + def exists(p: T => Bool): Bool = (this map p).fold(Bool(false))(_||_) + // def contains[T <: Bits](x: T): Bool = this.exists(_ === x) + def count(p: T => Bool): UInt = PopCount((this map p).toSeq) + + private def indexWhereHelper(p: T => Bool) = this map p zip (0 until length).map(i => UInt(i)) + def indexWhere(p: T => Bool): UInt = PriorityMux(indexWhereHelper(p)) + def lastIndexWhere(p: T => Bool): UInt = PriorityMux(indexWhereHelper(p).reverse) + def onlyIndexWhere(p: T => Bool): UInt = Mux1H(indexWhereHelper(p)) +} + +object chiselCast { + def apply[S <: Data, T <: Bits](i: S)(gen: => T): T = { + val x = gen + pushCommand(DefWire(x.defd.id, x.toType)) + val b = i.toBits + pushCommand(Connect(x.ref, b.ref)) + x + } +} + +import Literal._ + +class MInt(val value: String, val width: Int) extends Data(NO_DIR) { + def cloneTypeWidth(width: Int): this.type = cloneType + def collectElts: Unit = { } + override def dir: Direction = NO_DIR + override def setDir(dir: Direction): Unit = { } + override def toType: Type = UIntType(UnknownWidth(), isFlip) + override def getWidth: Int = width + override def flatten: Array[Bits] = Array[Bits](Bits(0)) + override def cloneType: this.type = + new MInt(value, width).asInstanceOf[this.type] + def fromInt(x: BigInt): MInt = MInt(x.toString(2), -1).asInstanceOf[this.type] + val (bits, mask, swidth) = parseLit(value) + def zEquals(other: Bits): Bool = + (Bits(toLitVal(mask, 2)) & other) === Bits(toLitVal(bits, 2)) + def === (other: Bits): Bool = zEquals(other) + def !== (other: Bits): Bool = !zEquals(other) +} + +object MInt { + def mintLit(value: String, width: Int) = new MInt(value, width) + def apply(value: String, width: Int) = mintLit(value, width) + def apply(value: String) = mintLit(value, -1) +} + +abstract class Element(dirArg: Direction, val width: Int) extends Data(dirArg) { + def collectElts: Unit = { } + override def getWidth: Int = width +} + +abstract class Bits(dirArg: Direction, width: Int) extends Element(dirArg, width) { + var litValue: Option[BigInt] = Some(-1) + + def litValueGet: BigInt = litValue.getOrElse(-1) + + override def flatten: Array[Bits] = Array[Bits](this) + + def apply(x: UInt): Bool = + apply(x.litValueGet) + + def apply(x: BigInt): Bool = { + val d = new Bool(dir) + pushCommand(DefPrim(d.defd.id, d.toType, BitSelectOp, Array(this.ref), Array(x))) + d + } + def apply(x: UInt, y: UInt): this.type = + apply(x.litValueGet, y.litValueGet) + + def apply(x: BigInt, y: BigInt): this.type = { + val d = cloneTypeWidth(-1) + pushCommand(DefPrim(d.defd.id, d.toType, BitsExtractOp, Array(this.ref), Array(x, y))) + d + } + + def :=(other: Bits) = + pushCommand(ConnectPad(this.ref, other.ref)) + + override def fromBits(n: Bits): this.type = { + val res = Wire(this.cloneType) + res := n + res.asInstanceOf[this.type] + } + + private def bits_unop(op: PrimOp): Bits = { + val d = cloneTypeWidth(-1) + pushCommand(DefPrim(d.defd.id, d.toType, op, Array(this.ref), NoLits)) + d + } + private def bits_binop(op: PrimOp, other: BigInt): Bits = { + val d = cloneTypeWidth(-1) + pushCommand(DefPrim(d.defd.id, d.toType, op, Array(this.ref), Array(other))) + d.asInstanceOf[Bits] + } + private def bits_binop_pad(op: PrimOp, other: Bits): Bits = { + val d = cloneTypeWidth(-1) + pushCommand(DefPrimPad(d.defd.id, d.toType, op, Array(this.ref, other.ref), NoLits)) + d + } + private def bits_binop(op: PrimOp, other: Bits): Bits = { + val d = cloneTypeWidth(-1) + pushCommand(DefPrim(d.defd.id, d.toType, op, Array(this.ref, other.ref), NoLits)) + d + } + private def bits_compop_pad(op: PrimOp, other: Bits): Bool = { + val d = new Bool(dir) + pushCommand(DefPrimPad(d.defd.id, d.toType, op, Array(this.ref, other.ref), NoLits)) + d + } + + def unary_- = bits_unop(NegOp) + def + (other: Bits) = bits_binop_pad(AddOp, other) + def +% (other: Bits) = bits_binop_pad(AddModOp, other) + def - (other: Bits) = bits_binop_pad(SubOp, other) + def -% (other: Bits) = bits_binop_pad(SubModOp, other) + def * (other: Bits) = bits_binop_pad(TimesOp, other) + def / (other: Bits) = bits_binop_pad(DivideOp, other) + def % (other: Bits) = bits_binop_pad(ModOp, other) + def << (other: BigInt) = bits_binop(ShiftLeftOp, other) + def << (other: Bits) = bits_binop(DynamicShiftLeftOp, other) + def >> (other: BigInt) = bits_binop(ShiftRightOp, other) + def >> (other: Bits) = bits_binop(DynamicShiftRightOp, other) + def unary_~(): Bits = bits_unop(BitNotOp) + + def & (other: Bits) = bits_binop_pad(BitAndOp, other) + def | (other: Bits) = bits_binop_pad(BitOrOp, other) + def ^ (other: Bits) = bits_binop_pad(BitXorOp, other) + + def < (other: Bits) = bits_compop_pad(LessOp, other) + def > (other: Bits) = bits_compop_pad(GreaterOp, other) + def === (other: Bits) = bits_compop_pad(EqualOp, other) + def !== (other: Bits) = bits_compop_pad(NotEqualOp, other) + def <= (other: Bits) = bits_compop_pad(LessEqOp, other) + def >= (other: Bits) = bits_compop_pad(GreaterEqOp, other) + def pad (other: BigInt) = bits_binop(PadOp, other) + + private def bits_redop(op: PrimOp): Bool = { + val d = new Bool(dir) + pushCommand(DefPrim(d.defd.id, d.toType, op, Array(this.ref), NoLits)) + d + } + + def orR = bits_redop(OrReduceOp) + def andR = bits_redop(AndReduceOp) + def xorR = bits_redop(XorReduceOp) + + def bitSet(off: BigInt, dat: Bits): Bits = { + val bit = UInt(1, 1) << off + this & ~bit | dat.toSInt & bit + } + + def toBools: Vec[Bool] = Vec.tabulate(this.getWidth)(i => this(i)) + + def fromInt(x: BigInt): this.type; + + def toSInt(): SInt = chiselCast(this){SInt()}; + + def toUInt(): UInt = chiselCast(this){UInt()}; +} + +import UInt._ + +object Bits { + def apply(dir: Direction = OUTPUT, width: Int = -1) = new UInt(dir, width) + def apply(value: BigInt, width: Int) = uintLit(value, width) + def apply(value: BigInt) = uintLit(value, -1) +} + +abstract trait Num[T <: Data] { + // def << (b: T): T; + // def >> (b: T): T; + //def unary_-(): T; + def + (b: T): T; + def * (b: T): T; + def / (b: T): T; + def % (b: T): T; + def - (b: T): T; + def < (b: T): Bool; + def <= (b: T): Bool; + def > (b: T): Bool; + def >= (b: T): Bool; + + def min(b: T): T = Mux(this < b, this.asInstanceOf[T], b) + def max(b: T): T = Mux(this < b, b, this.asInstanceOf[T]) +} + +class UInt(dir: Direction, width: Int) extends Bits(dir, width) with Num[UInt] { + override def cloneTypeWidth(w: Int): this.type = + new UInt(dir, w).asInstanceOf[this.type] + override def cloneType : this.type = cloneTypeWidth(width) + + def toType: Type = + UIntType(if (width == -1) UnknownWidth() else IntWidth(width), isFlipVar) + + def fromInt(x: BigInt): this.type = UInt(x).asInstanceOf[this.type] + + override def makeLit(value: BigInt, width: Int): this.type = + UInt(value, width).asInstanceOf[this.type] + + private def uint_unop(op: PrimOp): UInt = { + val d = cloneTypeWidth(-1) + pushCommand(DefPrim(d.defd.id, d.toType, op, Array(this.ref), NoLits)) + d + } + private def uint_binop(op: PrimOp, other: UInt): UInt = { + val d = cloneTypeWidth(-1) + pushCommand(DefPrim(d.defd.id, d.toType, op, Array(this.ref, other.ref), NoLits)) + d + } + private def uint_binop_pad(op: PrimOp, other: UInt): UInt = { + val d = cloneTypeWidth(-1) + pushCommand(DefPrimPad(d.defd.id, d.toType, op, Array(this.ref, other.ref), NoLits)) + d + } + private def uint_binop(op: PrimOp, other: BigInt): UInt = { + val d = cloneTypeWidth(-1) + pushCommand(DefPrim(d.defd.id, d.toType, op, Array(this.ref), Array(other))) + d + } + private def uint_compop_pad(op: PrimOp, other: UInt): Bool = { + val d = new Bool(dir) + pushCommand(DefPrimPad(d.defd.id, d.toType, op, Array(this.ref, other.ref), NoLits)) + d + } + + override def unary_- = uint_unop(NegOp) + def + (other: UInt) = uint_binop_pad(AddOp, other) + def +% (other: UInt) = uint_binop_pad(AddModOp, other) + def - (other: UInt) = uint_binop_pad(SubOp, other) + def -% (other: UInt) = uint_binop_pad(SubModOp, other) + def * (other: UInt) = uint_binop_pad(TimesOp, other) + def / (other: UInt) = uint_binop_pad(DivideOp, other) + def % (other: UInt) = uint_binop_pad(ModOp, other) + override def << (other: BigInt) = uint_binop(ShiftLeftOp, other) + def << (other: UInt) = uint_binop(DynamicShiftLeftOp, other) + override def >> (other: BigInt) = uint_binop(ShiftRightOp, other) + def >> (other: UInt) = uint_binop(DynamicShiftRightOp, other) + + override def unary_~(): UInt = uint_unop(BitNotOp) + def & (other: UInt) = uint_binop_pad(BitAndOp, other) + def | (other: UInt) = uint_binop_pad(BitOrOp, other) + def ^ (other: UInt) = uint_binop_pad(BitXorOp, other) + + def < (other: UInt) = uint_compop_pad(LessOp, other) + def > (other: UInt) = uint_compop_pad(GreaterOp, other) + def === (other: UInt) = uint_compop_pad(EqualOp, other) + def !== (other: UInt) = uint_compop_pad(NotEqualOp, other) + def <= (other: UInt) = uint_compop_pad(LessEqOp, other) + def >= (other: UInt) = uint_compop_pad(GreaterEqOp, other) + + override def pad (other: BigInt) = uint_binop(PadOp, other) +} + +object UInt { + def apply(dir: Direction = OUTPUT, width: Int = -1) = + new UInt(dir, width) + def uintLit(value: BigInt, width: Int) = { + val w = if(width == -1) max(bitLength(value), 1) else width + val b = new UInt(NO_DIR, w) + pushCommand(DefUInt(b.defd.id, value, w)) + b + } + def apply(value: BigInt, width: Int) = uintLit(value, width) + def apply(value: BigInt) = uintLit(value, -1) +} + +class SInt(dir: Direction, width: Int) extends Bits(dir, width) with Num[SInt] { + override def cloneTypeWidth(w: Int): this.type = + new SInt(dir, w).asInstanceOf[this.type] + override def cloneType: this.type = cloneTypeWidth(width) + def toType: Type = + SIntType(if (width == -1) UnknownWidth() else IntWidth(width), isFlipVar) + + def fromInt(x: BigInt): this.type = SInt(x).asInstanceOf[this.type] + + override def makeLit(value: BigInt, width: Int): this.type = + SInt(value, width).asInstanceOf[this.type] + + private def sint_unop(op: PrimOp): SInt = { + val d = cloneTypeWidth(-1) + pushCommand(DefPrim(d.defd.id, d.toType, op, Array(this.ref), NoLits)) + d + } + private def sint_binop(op: PrimOp, other: BigInt): Bits = { + val d = cloneTypeWidth(-1) + pushCommand(DefPrim(d.defd.id, d.toType, op, Array(this.ref), Array(other))) + d + } + private def sint_binop(op: PrimOp, other: Bits): SInt = { + val d = cloneTypeWidth(-1) + pushCommand(DefPrim(d.defd.id, d.toType, op, Array(this.ref, other.ref), NoLits)) + d + } + private def sint_binop_pad(op: PrimOp, other: SInt): SInt = { + val d = cloneTypeWidth(-1) + pushCommand(DefPrimPad(d.defd.id, d.toType, op, Array(this.ref, other.ref), NoLits)) + d + } + private def sint_compop_pad(op: PrimOp, other: SInt): Bool = { + val d = new Bool(dir) + pushCommand(DefPrimPad(d.defd.id, d.toType, op, Array(this.ref, other.ref), NoLits)) + d + } + + override def unary_- = sint_unop(NegOp) + def + (other: SInt) = sint_binop_pad(AddOp, other) + def +% (other: SInt) = sint_binop_pad(AddModOp, other) + def - (other: SInt) = sint_binop_pad(SubOp, other) + def -% (other: SInt) = sint_binop_pad(SubModOp, other) + def * (other: SInt) = sint_binop_pad(TimesOp, other) + def / (other: SInt) = sint_binop_pad(DivideOp, other) + def % (other: SInt) = sint_binop_pad(ModOp, other) + override def << (other: BigInt) = sint_binop(ShiftLeftOp, other) + def << (other: UInt) = sint_binop(DynamicShiftLeftOp, other) + override def >> (other: BigInt) = sint_binop(ShiftRightOp, other) + def >> (other: UInt) = sint_binop(DynamicShiftRightOp, other) + + override def unary_~(): SInt = sint_unop(BitNotOp) + def & (other: SInt) = sint_binop_pad(BitAndOp, other) + def | (other: SInt) = sint_binop_pad(BitOrOp, other) + def ^ (other: SInt) = sint_binop_pad(BitXorOp, other) + + def < (other: SInt) = sint_compop_pad(LessOp, other) + def > (other: SInt) = sint_compop_pad(GreaterOp, other) + def === (other: SInt) = sint_compop_pad(EqualOp, other) + def !== (other: SInt) = sint_compop_pad(NotEqualOp, other) + def <= (other: SInt) = sint_compop_pad(LessEqOp, other) + def >= (other: SInt) = sint_compop_pad(GreaterEqOp, other) + + override def pad (other: BigInt) = sint_binop(PadOp, other) +} + +object SInt { + def apply(dir: Direction = OUTPUT, width: Int = -1) = + new SInt(dir, width) + def sintLit(value: BigInt, width: Int) = { + val w = if (width == -1) bitLength(value) + 1 else width + val b = new SInt(NO_DIR, w) + pushCommand(DefSInt(b.defd.id, value, w)) + b + } + def apply(value: BigInt, width: Int) = sintLit(value, width) + def apply(value: BigInt) = sintLit(value, -1) +} + +class Bool(dir: Direction) extends UInt(dir, 1) { + override def cloneTypeWidth(w: Int): this.type = new Bool(dir).asInstanceOf[this.type] + override def cloneType: this.type = cloneTypeWidth(1) + + override def fromInt(x: BigInt): this.type = Bool(x).asInstanceOf[this.type] + + override def makeLit(value: BigInt, width: Int): this.type = + Bool(value).asInstanceOf[this.type] + + def || (other: Bool) = { + val d = new Bool(dir) + pushCommand(DefPrim(d.defd.id, d.toType, OrOp, Array(this.ref, other.ref), NoLits)) + d + } + def && (other: Bool) = { + val d = new Bool(dir) + pushCommand(DefPrim(d.defd.id, d.toType, AndOp, Array(this.ref, other.ref), NoLits)) + d + } + def unary_! (): Bool = { + val d = new Bool(dir) + pushCommand(DefPrim(d.defd.id, d.toType, NotOp, Array(this.ref), NoLits)) + d + } +} +object Bool { + def apply(dir: Direction) : Bool = + new Bool(dir) + def apply() : Bool = + apply(NO_DIR) + def apply(value: BigInt) : Bool = { + val b = new Bool(NO_DIR) + pushCommand(DefUInt(b.defd.id, value, 1)) + b + } + def apply(value: Boolean) : Bool = + apply(if (value) 1 else 0) +} + +object Mux { + def apply[T <: Data](cond: Bool, con: T, alt: T): T = { + val r = con.cloneTypeWidth(-1) + val args = Array(cond.ref, con.ref, alt.ref) + con match { + case t: Bits => + pushCommand(DefPrimPad(r.defd.id, r.toType, MultiplexOp, args, NoLits)) + case _ => + pushCommand(DefPrim(r.defd.id, r.toType, MultiplexOp, args, NoLits)) + } + r.asInstanceOf[T] + } +} + +object Cat { + def apply[T <: Bits](a: T, r: T*): T = apply(a :: r.toList) + def apply[T <: Bits](r: Seq[T]): T = doCat(r) + private def doCat[T <: Data](r: Seq[T]): T = { + if (r.tail.isEmpty) + r.head + else { + val l = doCat(r.slice(0, r.length/2)) + val h = doCat(r.slice(r.length/2, r.length)) + val d = l.cloneType + pushCommand(DefPrim(d.id, d.toType, ConcatOp, Array(l.ref, h.ref), NoLits)) + d + } + } +} + +object Bundle { + val keywords = HashSet[String]("elements", "flip", "toString", + "flatten", "binding", "asInput", "asOutput", "unary_$tilde", + "unary_$bang", "unary_$minus", "cloneType", "toUInt", "toBits", + "toBool", "toSInt", "asDirectionless") +} + +class Bundle(dirArg: Direction = NO_DIR) extends Aggregate(dirArg) { + def toPorts: Array[Port] = + elts.map(d => d.toPort).toArray + def toType: BundleType = + BundleType(this.toPorts, isFlipVar) + + override def flatten: Array[Bits] = + elts.map(_.flatten).reduce(_ ++ _) + override def getWidth: Int = + flatten.map(_.getWidth).reduce(_ + _) + + val elts = ArrayBuffer[Data]() + def collectElts: Unit = { + elts.clear() + for (m <- getClass.getDeclaredMethods) { + val name = m.getName + + val modifiers = m.getModifiers(); + val types = m.getParameterTypes() + var isInterface = false; + var isFound = false; + val rtype = m.getReturnType(); + var c = rtype; + val sc = Class.forName("Chisel.Data"); + do { + if (c == sc) { + isFound = true; isInterface = true; + } else if (c == null || c == Class.forName("java.lang.Object")) { + isFound = true; isInterface = false; + } else { + c = c.getSuperclass(); + } + } while (!isFound); + if (types.length == 0 && !isStatic(modifiers) && isInterface + && !(Bundle.keywords contains name)) { + val obj = m.invoke(this) + obj match { + case data: Data => + setFieldForId(id, data.id, name) + data.collectElts + elts += data + case _ => () + } + } + } + + elts.sortWith { (a, b) => a.id < b.id } + } + + override def cloneType: this.type = { + try { + val constructor = this.getClass.getConstructors.head + val res = constructor.newInstance(Array.fill(constructor.getParameterTypes.size)(null):_*) + val rest = res.asInstanceOf[this.type] + rest.collectElts + rest + } catch { + case npe: java.lang.reflect.InvocationTargetException if npe.getCause.isInstanceOf[java.lang.NullPointerException] => + // throwException("Parameterized Bundle " + this.getClass + " needs clone method. You are probably using an anonymous Bundle object that captures external state and hence is un-cloneable", npe) + error("BAD") + case e: java.lang.Exception => + error("BAD") + // throwException("Parameterized Bundle " + this.getClass + " needs clone method", e) + } + } +} + +object Module { + def apply[T <: Module](c: T): T = { + val cmd = popCommands + popScope + popModule + c.io.collectElts + c.setRefs + val ports = c.io.toPorts + val component = UniqueComponent(c.name, ports, cmd) + components += component + pushCommand(DefInstance(c.defd.id, component.name)) + c + } +} + +abstract class Module extends Id { + val parent = modulez.headOption + pushModule(this) + pushScope + pushCommands + addModule(this) + + def io: Bundle + def ref = getRefForId(id) + val reset = Bool().defd + setRefForId(reset.id, "reset") + + def name = { + // getClass.getName.replace('.', '_') + getClass.getName.split('.').last + } + def debugName: String = { + val p = parent.getOrElse(null) + name + (if (p == null) "" else ("__" + p.debugName)) + } + + def setRefs { + setRefForId(io.id, "this") + + for (m <- getClass.getDeclaredMethods) { + val name = m.getName() + val types = m.getParameterTypes() + if (types.length == 0) { + val obj = m.invoke(this) + obj match { + case module: Module => + setRefForId(module.id, name) + module.setRefs + case bundle: Bundle => + if (name != "io") { + setRefForId(bundle.id, name) + } + case mem: Mem[_] => + setRefForId(mem.t.id, name) + case vec: Vec[_] => + setRefForId(vec.id, name) + case data: Data => + setRefForId(data.id, name) + // ignore anything not of those types + case _ => null + } + } + } + } +} + +object when { + def execBlock(block: => Unit): Command = { + pushScope + pushCommands + block + val cmd = popCommands + popScope + cmd + } + def execWhen(cond: => Bool)(block: => Unit) { + val pred = cond.ref + val cmd = execBlock(block) + pushCommand(Conditionally(pred, cmd, EmptyCommand())) + } + def apply(cond: => Bool)(block: => Unit): when = { + execWhen(cond){ block } + new when + } +} + +import when._ + +class when { + def elsewhen (cond: => Bool)(block: => Unit): when = { + this.otherwise { + when.execWhen(cond) { block } + } + new when + } + + private def replaceCondition(cond: Conditionally, elsecmd: Command): Conditionally = { + cond.alt match { + // this is an elsewhen clause + // we have to go deeper + case newcond: Conditionally => + Conditionally(cond.pred, cond.conseq, + replaceCondition(newcond, elsecmd)) + // if the alt is empty, we've found the end + case empty: EmptyCommand => + Conditionally(cond.pred, cond.conseq, elsecmd) + // this shouldn't happen + case _ => + throw new Exception("Cannot replace non-empty else clause") + } + } + + def otherwise (block: => Unit) { + // first generate the body + val elsecmd = execBlock(block) + // now we look back and find the last Conditionally + val isConditionally = (x: Command) => { + x match { + case Conditionally(_, _, _) => true + case _ => false + } + } + // replace the last Conditionally with a new one with the + // same predicate and consequent but with the last alt replaced + // by the commands for the otherwise body + val i = commands.lastIndexWhere(isConditionally) + commands(i) = commands(i) match { + case cond: Conditionally => + replaceCondition(cond, elsecmd) + // this should never happen + case _ => throw new Exception("That's not a conditionally") + } + } +} + + +/// CHISEL IR EMITTER + +class Emitter { + var indenting = 0 + def withIndent(f: => String) = { + indenting += 1; + val res = f + indenting -= 1; + res + } + def join(parts: Array[String], sep: String) = + parts.foldLeft("")((s, p) => if (s == "") p else s + sep + p) + def join0(parts: Array[String], sep: String) = + parts.foldLeft("")((s, p) => s + sep + p) + def newline = + "\n" + join((0 until indenting).map(x => " ").toArray, "") + def emitDir(e: Direction, isTop: Boolean): String = + if (isTop) (e.name + " ") else if (e == INPUT) "flip " else "" + def emit(e: PrimOp): String = e.name + def emit(e: Alias): String = e.fullname + def emitPort(e: Port, isTop: Boolean): String = + emitDir(e.dir, isTop) + getRefForId(e.id).name + " : " + emitType(e.kind) + def emit(e: Width): String = { + e match { + case e: UnknownWidth => "" + case e: IntWidth => "(" + e.value.toString + ")" + } + } + def emitType(e: Type): String = { + e match { + case e: UnknownType => "?" + case e: UIntType => "UInt" + emit(e.width) + case e: SIntType => "SInt" + emit(e.width) + case e: BundleType => "{" + join(e.ports.map(x => emitPort(x, false)), ", ") + "}" + case e: VectorType => emitType(e.kind) + "[" + e.size + "]" + } + } + def emit(e: Command): String = { + e match { + case e: DefUInt => "node " + e.name + " = UInt(" + e.value + (if (e.width == -1) "" else ", " + e.width) + ")" + case e: DefSInt => "node " + e.name + " = SInt(" + e.value + (if (e.width == -1) "" else ", " + e.width) + ")" + case e: DefFlo => "node " + e.name + " = Flo(" + e.value + ")" + case e: DefDbl => "node " + e.name + " = Dbl(" + e.value + ")" + case e: DefPrim => "node " + e.name + " = " + emit(e.op) + "(" + join(e.args.map(x => emit(x)) ++ e.lits.map(x => x.toString), ", ") + ")" + case e: DefPrimPad => "node " + e.name + " = " + emit(e.op) + "(" + join(e.args.map(x => "Pad(" + emit(x) + ",?)") ++ e.lits.map(x => x.toString), ", ") + ")" + case e: DefWire => "wire " + e.name + " : " + emitType(e.kind) + case e: DefRegister => "reg " + e.name + " : " + emitType(e.kind) + case e: DefMemory => "mem " + e.name + " : " + emitType(e.kind) + "[" + e.size + "]"; + // case e: DefVector => "vec " + e.name + " : " + emit(e.kind) + "(" + join(e.args.map(x => emit(x)).toArray[String], " ") + ")" + case e: DefAccessor => "accessor " + e.name + " = " + emit(e.source) + "[" + emit(e.index) + "]" + case e: DefInstance => { + val mod = modules(e.id) + // update all references to the modules ports + setRefForId(mod.io.id, e.name, true) + "inst " + e.name + " of " + e.module + } + case e: Conditionally => "when " + emit(e.pred) + " : " + withIndent{ emit(e.conseq) } + (if (e.alt.isInstanceOf[EmptyCommand]) "" else newline + "else : " + withIndent{ emit(e.alt) }) + case e: Begin => join0(e.body.map(x => emit(x)), newline) + case e: Connect => emit(e.loc) + " := " + emit(e.exp) + case e: ConnectPad => emit(e.loc) + " := Pad(" + emit(e.exp) + ",?)" + case e: ConnectInit => emit(e.loc) + ".init := Pad(" + emit(e.exp) + ",?)" + case e: ConnectInitIndex => emit(e.loc) + ".init." + e.index + " := Pad(" + emit(e.exp) + ",?)" + case e: EmptyCommand => "skip" + } + } + def emit(e: Component): String = { + withIndent{ "module " + e.name + " : " + + join0(e.ports.map(x => emitPort(x, true)), newline) + + newline + emit(e.body) } + } + def emit(e: Circuit): String = + withIndent{ "circuit " + e.main + " : " + join0(e.components.map(x => emit(x)), newline) } +} diff --git a/src/main/scala/Driver.scala b/src/main/scala/Driver.scala new file mode 100644 index 00000000..1f3f49b8 --- /dev/null +++ b/src/main/scala/Driver.scala @@ -0,0 +1,421 @@ +/* + Copyright (c) 2011, 2012, 2013, 2014 The Regents of the University of + California (Regents). All Rights Reserved. Redistribution and use in + source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above + copyright notice, this list of conditions and the following + two paragraphs of disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + two paragraphs of disclaimer in the documentation and/or other materials + provided with the distribution. + * Neither the name of the Regents nor the names of its contributors + may be used to endorse or promote products derived from this + software without specific prior written permission. + + IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, + SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, + ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF + REGENTS HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF + ANY, PROVIDED HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION + TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR + MODIFICATIONS. +*/ + +package Chisel +import Builder._ +import Direction._ +import ChiselError._ + +import collection.mutable.{ArrayBuffer, HashSet, HashMap, Stack, LinkedHashSet, Queue => ScalaQueue} +import scala.math.min +import java.io.File +import java.io.InputStream +import java.io.OutputStream +import java.io.PrintStream + +trait FileSystemUtilities { + /** Ensures a directory *dir* exists on the filesystem. */ + def ensureDir(dir: String): String = { + val d = dir + (if (dir == "" || dir(dir.length-1) == '/') "" else "/") + new File(d).mkdirs() + d + } + + def createOutputFile(name: String): java.io.FileWriter = { + val baseDir = ensureDir(Driver.targetDir) + new java.io.FileWriter(baseDir + name) + } +} + +class ChiselException(message: String, cause: Throwable) extends Exception(message, cause) + +object throwException { + def apply(s: String, t: Throwable = null) = { + val xcpt = new ChiselException(s, t) + findFirstUserLine(xcpt.getStackTrace) foreach { u => xcpt.setStackTrace(Array(u)) } + throw xcpt + } +} + +object chiselMain { + val wrapped = true + val unwrapped = false + + def apply[T <: Module](args: Array[String], gen: () => T): (Circuit, T) = + Driver(args, gen, wrapped) + + def apply[T <: Module](args: Array[String], gen: () => T, ftester: T => Tester[T]): (Circuit, T) = + Driver(args, gen, ftester, wrapped) + + // Assumes gen needs to be wrapped in Module() + def run[T <: Module] (args: Array[String], gen: () => T): (Circuit, T) = + Driver(args, gen, unwrapped) + + def run[T <: Module] (args: Array[String], gen: () => T, ftester: T => Tester[T]): (Circuit, T) = + Driver(args, gen, ftester, unwrapped) +} + +//Is this antiquated? +object chiselMainTest { + def apply[T <: Module](args: Array[String], gen: () => T)(tester: T => Tester[T]): (Circuit, T) = + chiselMain(args, gen, tester) +} + +object Driver extends FileSystemUtilities{ + def apply[T <: Module](args: Array[String], gen: () => T, wrapped:Boolean = true): (Circuit, T) = { + initChisel(args) + try { + execute(gen) /* else executeUnwrapped(gen) */ + } finally { + ChiselError.report + if (ChiselError.hasErrors && !getLineNumbers) { + println("Re-running Chisel in debug mode to obtain erroneous line numbers...") + // apply(args :+ "--lineNumbers", gen, wrapped) + } + } + } + + def apply[T <: Module](args: Array[String], gen: () => T, + ftester: T => Tester[T], wrapped:Boolean): (Circuit, T) = { + val (circuit, mod) = apply(args, gen, wrapped) + if (isTesting) test(mod, ftester) + (circuit, mod) + } + + /* + private def executeUnwrapped[T <: Module](gen: () => T): T = { + if (!chiselConfigMode.isEmpty && !chiselConfigClassName.isEmpty) { + val name = appendString(chiselProjectName,chiselConfigClassName) + val config = try { + Class.forName(name).newInstance.asInstanceOf[ChiselConfig] + } catch { + case e: java.lang.ClassNotFoundException => + throwException("Could not find the ChiselConfig subclass you asked for (\"" + + name + "\"), did you misspell it?", e) + } + val world = if(chiselConfigMode.get == "collect") { + new Collector(config.topDefinitions,config.knobValues) + } else { new Instance(config.topDefinitions,config.knobValues) } + val p = Parameters.root(world) + config.topConstraints.foreach(c => p.constrain(c)) + val c = execute(() => Module(gen())(p)) + if(chiselConfigMode.get == "collect") { + val v = createOutputFile(chiselConfigClassName.get + ".knb") + v.write(world.getKnobs) + v.close + val w = createOutputFile(chiselConfigClassName.get + ".cst") + w.write(world.getConstraints) + w.close + } + c + } + else { + execute(() => Module(gen())) + } + } + */ + + private def execute[T <: Module](gen: () => T): (Circuit, T) = { + val emitter = new Emitter + val (c, mod) = build{ Module(gen()) } + val s = emitter.emit( c ) + val filename = c.main + ".fir" + // println("FILENAME " + filename) + // println("S = " + s) + val out = createOutputFile(filename) + out.write(s) + /* + /* Params - If dumping design, dump space to pDir*/ + if (chiselConfigMode == None || chiselConfigMode.get == "instance") { + setTopComponent(c) + backend.elaborate(c) + if (isCompiling && isGenHarness) backend.compile(c) + if(chiselConfigDump && !Dump.dump.isEmpty) { + val w = createOutputFile(appendString(Some(topComponent.name),chiselConfigClassName) + ".prm") + w.write(Dump.getDump); w.close + } + } + */ + out.close() + (c, mod) + } + + private def test[T <: Module](mod: T, ftester: T => Tester[T]): Unit = { + var res = false + var tester: Tester[T] = null + try { + tester = ftester(mod) + } finally { + if (tester != null && tester.process != null) + res = tester.finish() + } + println(if (res) "PASSED" else "*** FAILED ***") + if(!res) throwException("Module under test FAILED at least one test vector.") + } + + def elapsedTime: Long = System.currentTimeMillis - startTime + + def initChisel(args: Array[String]): Unit = { + ChiselError.clear() + warnInputs = false + warnOutputs = false + saveConnectionWarnings = false + saveComponentTrace = false + dontFindCombLoop = false + isGenHarness = false + isDebug = false + getLineNumbers = false + isCSE = false + isIoDebug = true + isVCD = false + isVCDMem = false + isReportDims = false + targetDir = "." + components.clear() + sortedComps.clear() + compStack.clear() + stackIndent = 0 + printStackStruct.clear() + // blackboxes.clear() + chiselOneHotMap.clear() + chiselOneHotBitMap.clear() + isCompiling = false + isCheckingPorts = false + isTesting = false + isDebugMem = false + isSupportW0W = false + partitionIslands = false + lineLimitFunctions = 0 + minimumLinesPerFile = 0 + shadowRegisterInObject = false + allocateOnlyNeededShadowRegisters = false + compileInitializationUnoptimized = false + useSimpleQueue = false + parallelMakeJobs = 0 + isVCDinline = false + isSupportW0W = false + hasMem = false + hasSRAM = false + sramMaxSize = 0 + topComponent = null + // clocks.clear() + // implicitReset.isIo = true + // implicitReset.setName("reset") + // implicitClock = new Clock() + // implicitClock.setName("clk") + isInGetWidth = false + startTime = System.currentTimeMillis + modStackPushed = false + + readArgs(args) + } + + private def readArgs(args: Array[String]): Unit = { + var i = 0 + var backendName = "c" // Default backend is Cpp. + while (i < args.length) { + val arg = args(i) + arg match { + case "--Wall" => { + saveConnectionWarnings = true + saveComponentTrace = true + isCheckingPorts = true + } + case "--wi" => warnInputs = true + case "--wo" => warnOutputs = true + case "--wio" => {warnInputs = true; warnOutputs = true} + case "--Wconnection" => saveConnectionWarnings = true + case "--Wcomponent" => saveComponentTrace = true + case "--W0W" => isSupportW0W = true + case "--noW0W" => isSupportW0W = false + case "--noCombLoop" => dontFindCombLoop = true + case "--genHarness" => isGenHarness = true + case "--debug" => isDebug = true + case "--lineNumbers" => getLineNumbers = true + case "--cse" => isCSE = true + case "--ioDebug" => isIoDebug = true + case "--noIoDebug" => isIoDebug = false + case "--vcd" => isVCD = true + case "--vcdMem" => isVCDMem = true + case "--v" => backendName = "v" + // case "--moduleNamePrefix" => Backend.moduleNamePrefix = args(i + 1); i += 1 + case "--inlineMem" => isInlineMem = true + case "--noInlineMem" => isInlineMem = false + case "--assert" => isAssert = true + case "--noAssert" => isAssert = false + case "--debugMem" => isDebugMem = true + case "--partitionIslands" => partitionIslands = true + case "--lineLimitFunctions" => lineLimitFunctions = args(i + 1).toInt; i += 1 + case "--minimumLinesPerFile" => minimumLinesPerFile = args(i + 1).toInt; i += 1 + case "--shadowRegisterInObject" => shadowRegisterInObject = true + case "--allocateOnlyNeededShadowRegisters" => allocateOnlyNeededShadowRegisters = true + case "--compileInitializationUnoptimized" => compileInitializationUnoptimized = true + case "--useSimpleQueue" => useSimpleQueue = true + case "--parallelMakeJobs" => parallelMakeJobs = args(i + 1).toInt; i += 1 + case "--isVCDinline" => isVCDinline = true + case "--backend" => backendName = args(i + 1); i += 1 + case "--compile" => isCompiling = true + case "--test" => isTesting = true + case "--targetDir" => targetDir = args(i + 1); i += 1 + case "--include" => includeArgs = args(i + 1).split(' ').toList; i += 1 + case "--checkPorts" => isCheckingPorts = true + case "--reportDims" => isReportDims = true + //Jackhammer Flags + case "--configCollect" => chiselConfigMode = Some("collect"); chiselConfigClassName = Some(getArg(args(i+1),1)); chiselProjectName = Some(getArg(args(i+1),0)); i+=1; //dump constraints in dse dir + case "--configInstance" => chiselConfigMode = Some("instance"); chiselConfigClassName = Some(getArg(args(i+1),1)); chiselProjectName = Some(getArg(args(i+1),0)); i+=1; //use ChiselConfig to supply parameters + case "--configDump" => chiselConfigDump = true; //when using --configInstance, write Dump parameters to .prm file in targetDir + case "--dumpTestInput" => dumpTestInput = true + case "--testerSeed" => { + testerSeedValid = true + testerSeed = args(i+1).toLong + i += 1 + } + case "--emitTempNodes" => { + isDebug = true + emitTempNodes = true + } + /* + // Dreamer configuration flags + case "--numRows" => { + if (backend.isInstanceOf[FloBackend]) { + backend.asInstanceOf[FloBackend].DreamerConfiguration.numRows = args(i+1).toInt + } + i += 1 + } + case "--numCols" => { + if (backend.isInstanceOf[FloBackend]) { + backend.asInstanceOf[FloBackend].DreamerConfiguration.numCols = args(i+1).toInt + } + i += 1 + } + */ + case any => ChiselError.warning("'" + arg + "' is an unknown argument.") + } + i += 1 + } + // Check for bogus flags + if (!isVCD) { + isVCDinline = false + } + // Set the backend after we've interpreted all the arguments. + // (The backend may want to configure itself based on the arguments.) + backend = backendName match { + case "v" => new VerilogBackend + case "c" => new CppBackend + case "flo" => new FloBackend + case "dot" => new DotBackend + case "fpga" => new FPGABackend + case "sysc" => new SysCBackend + case _ => Class.forName(backendName).newInstance.asInstanceOf[Backend] + } + } + + var warnInputs = false + var warnOutputs = false + var saveConnectionWarnings = false + var saveComponentTrace = false + var dontFindCombLoop = false + var isDebug = false + var getLineNumbers = false + var isCSE = false + var isIoDebug = true + var isVCD = false + var isVCDMem = false + var isInlineMem = true + var isGenHarness = false + var isReportDims = false + var includeArgs: List[String] = Nil + var targetDir: String = null + var isCompiling = false + var isCheckingPorts = false + var isTesting = false + var isAssert = true + var isDebugMem = false + var partitionIslands = false + var lineLimitFunctions = 0 + var minimumLinesPerFile = 0 + var shadowRegisterInObject = false + var allocateOnlyNeededShadowRegisters = false + var compileInitializationUnoptimized = false + var useSimpleQueue = false + var parallelMakeJobs = 0 + var isVCDinline = false + var isSupportW0W = false + var hasMem = false + var hasSRAM = false + var sramMaxSize = 0 + var backend: Backend = null + var topComponent: Module = null + val components = ArrayBuffer[Module]() + val sortedComps = ArrayBuffer[Module]() + // val blackboxes = ArrayBuffer[BlackBox]() + val chiselOneHotMap = HashMap[(UInt, Int), UInt]() + val chiselOneHotBitMap = HashMap[(Bits, Int), Bool]() + val compStack = Stack[Module]() + // val parStack = new Stack[Parameters] + var stackIndent = 0 + val printStackStruct = ArrayBuffer[(Int, Module)]() + // val clocks = ArrayBuffer[Clock]() + // val implicitReset = Bool(INPUT) + // var implicitClock: Clock = null + var isInGetWidth: Boolean = false + var modStackPushed: Boolean = false + var modAdded: Boolean = false + var startTime = 0L + /* ChiselConfig flags */ + var chiselConfigClassName: Option[String] = None + var chiselProjectName: Option[String] = None + var chiselConfigMode: Option[String] = None + var chiselConfigDump: Boolean = false + + def appendString(s1:Option[String],s2:Option[String]):String = { + if(s1.isEmpty && s2.isEmpty) "" else { + if(!s1.isEmpty) { + s1.get + (if(!s2.isEmpty) "." + s2.get else "") + } else { + if(!s2.isEmpty) s2.get else "" + } + } + } + def getArg(s:String,i:Int):String = s.split('.')(i) + + // Setting this to TRUE will case the test harness to print its + // standard input stream to a file. + var dumpTestInput = false + + // Setting this to TRUE will initialize the tester's RNG with the + // seed below. + var testerSeedValid = false + var testerSeed = System.currentTimeMillis() + + // Setting this to TRUE will result in temporary values (ie, nodes + // named "T*") to be emited to the VCD file. + var emitTempNodes = false +} diff --git a/src/main/scala/Enum.scala b/src/main/scala/Enum.scala new file mode 100644 index 00000000..3bc24220 --- /dev/null +++ b/src/main/scala/Enum.scala @@ -0,0 +1,44 @@ +/* + Copyright (c) 2011, 2012, 2013, 2014 The Regents of the University of + California (Regents). All Rights Reserved. Redistribution and use in + source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above + copyright notice, this list of conditions and the following + two paragraphs of disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + two paragraphs of disclaimer in the documentation and/or other materials + provided with the distribution. + * Neither the name of the Regents nor the names of its contributors + may be used to endorse or promote products derived from this + software without specific prior written permission. + + IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, + SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, + ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF + REGENTS HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF + ANY, PROVIDED HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION + TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR + MODIFICATIONS. +*/ + +package Chisel +import Literal._ + +object Enum { + /** create n enum values of given type */ + def apply[T <: Bits](nodeType: T, n: Int): List[T] = (Range(0, n, 1).map(x => (Lit(x, sizeof(n-1))(nodeType)))).toList; + + /** create enum values of given type and names */ + def apply[T <: Bits](nodeType: T, l: Symbol *): Map[Symbol, T] = (l.toList zip (Range(0, l.length, 1).map(x => Lit(x, sizeof(l.length-1))(nodeType)))).toMap; + + /** create enum values of given type and names */ + def apply[T <: Bits](nodeType: T, l: List[Symbol]): Map[Symbol, T] = (l zip (Range(0, l.length, 1).map(x => Lit(x, sizeof(l.length-1))(nodeType)))).toMap; + +} diff --git a/src/main/scala/Error.scala b/src/main/scala/Error.scala new file mode 100644 index 00000000..1ad5414d --- /dev/null +++ b/src/main/scala/Error.scala @@ -0,0 +1,167 @@ +/* + Copyright (c) 2011, 2012, 2013, 2014 The Regents of the University of + California (Regents). All Rights Reserved. Redistribution and use in + source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above + copyright notice, this list of conditions and the following + two paragraphs of disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + two paragraphs of disclaimer in the documentation and/or other materials + provided with the distribution. + * Neither the name of the Regents nor the names of its contributors + may be used to endorse or promote products derived from this + software without specific prior written permission. + + IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, + SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, + ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF + REGENTS HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF + ANY, PROVIDED HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION + TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR + MODIFICATIONS. +*/ + +package Chisel +import scala.collection.mutable.ArrayBuffer + +/** This Singleton implements a log4j compatible interface. + It is used through out the Chisel package to report errors and warnings + detected at runtime. + */ +object ChiselError { + var hasErrors: Boolean = false; + val ChiselErrors = new ArrayBuffer[ChiselError]; + + def clear() { + ChiselErrors.clear() + hasErrors = false + } + + /** emit an error message */ + def error(mf: => String, line: StackTraceElement) { + hasErrors = true + ChiselErrors += new ChiselError(() => mf, line) + } + + def error(m: String) { + val stack = Thread.currentThread().getStackTrace + error(m, findFirstUserLine(stack) getOrElse stack(0)) + } + + /** Emit an informational message + (useful to track long running passes) */ + def info(m: String): Unit = + println(tag("info", Console.MAGENTA) + " [%2.3f] ".format(Driver.elapsedTime/1e3) + m) + + /** emit a warning message */ + def warning(m: => String) { + val stack = Thread.currentThread().getStackTrace + ChiselErrors += new ChiselError(() => m, + findFirstUserLine(stack) getOrElse stack(0), 1) + } + + def findFirstUserLine(stack: Array[StackTraceElement]): Option[StackTraceElement] = { + findFirstUserInd(stack) map { stack(_) } + } + + def findFirstUserInd(stack: Array[StackTraceElement]): Option[Int] = { + def isUserCode(ste: StackTraceElement): Boolean = { + val className = ste.getClassName() + try { + val cls = Class.forName(className) + if( cls.getSuperclass() == classOf[Module] ) { + true + } else { + /* XXX Do it the old way until we figure if it is safe + to remove from Node.scala + var line: StackTraceElement = findFirstUserLine(Thread.currentThread().getStackTrace) + */ + val dotPos = className.lastIndexOf('.') + if( dotPos > 0 ) { + (className.subSequence(0, dotPos) != "Chisel") && !className.contains("scala") && + !className.contains("java") && !className.contains("$$") + } else { + false + } + } + } catch { + case e: java.lang.ClassNotFoundException => false + } + } + val idx = stack.indexWhere(isUserCode) + if(idx < 0) { + println("COULDN'T FIND LINE NUMBER (" + stack(1) + ")") + None + } else { + Some(idx) + } + } + + // Print stack frames up to and including the "user" stack frame. + def printChiselStackTrace() { + val stack = Thread.currentThread().getStackTrace + val idx = ChiselError.findFirstUserInd(stack) + idx match { + case None => {} + case Some(x) => for (i <- 0 to x) println(stack(i)) + } + } + + /** Prints error messages generated by Chisel at runtime. */ + def report() { + if (!ChiselErrors.isEmpty) { + for(err <- ChiselErrors) err.print; + } + } + + /** Throws an exception if there has been any error recorded + before this point. */ + def checkpoint() { + if(hasErrors) { + throw new IllegalStateException( + Console.UNDERLINED + "CODE HAS " + + Console.UNDERLINED + Console.BOLD + ChiselErrors.filter(_.isError).length + Console.RESET + + Console.UNDERLINED + " " + + Console.UNDERLINED + Console.RED + "ERRORS" + Console.RESET + + Console.UNDERLINED + " and " + + Console.UNDERLINED + Console.BOLD + ChiselErrors.filter(_.isWarning).length + Console.RESET + + Console.UNDERLINED + " " + + Console.UNDERLINED + Console.YELLOW + "WARNINGS" + Console.RESET) + } + } + + def tag(name: String, color: String): String = + s"[${color}${name}${Console.RESET}]" +} + +class ChiselError(val errmsgFun: () => String, val errline: StackTraceElement, +val errlevel: Int = 0) { + + val level = errlevel + val line = errline + val msgFun = errmsgFun + + def isError = (level == 0) + def isWarning = (level == 1) + + def print() { + /* Following conventions for error formatting */ + val levelstr = + if (isError) ChiselError.tag("error", Console.RED) + else ChiselError.tag("warn", Console.YELLOW) + if( line != null ) { + println(levelstr + " " + line.getFileName + ":" + + line.getLineNumber + ": " + msgFun() + + " in class " + line.getClassName) + } else { + println(levelstr + ": " + msgFun()) + } + } +} diff --git a/src/main/scala/FP.scala b/src/main/scala/FP.scala new file mode 100644 index 00000000..5f6cf579 --- /dev/null +++ b/src/main/scala/FP.scala @@ -0,0 +1,319 @@ +/* + Copyright (c) 2011, 2012, 2013, 2014 The Regents of the University of + California (Regents). All Rights Reserved. Redistribution and use in + source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above + copyright notice, this list of conditions and the following + two paragraphs of disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + two paragraphs of disclaimer in the documentation and/or other materials + provided with the distribution. + * Neither the name of the Regents nor the names of its contributors + may be used to endorse or promote products derived from this + software without specific prior written permission. + + IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, + SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, + ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF + REGENTS HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF + ANY, PROVIDED HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION + TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR + MODIFICATIONS. +*/ + +package Chisel +import Chisel._ +import Builder._ +import ChiselError._ +import Commands.NoLits + +/// FLO + +object Flo { + def apply(x: Float): Flo = floLit(x) + def apply(x: Double): Flo = Flo(x.toFloat) + def floLit(value: Float): Flo = { + val b = new Flo(NO_DIR) + pushCommand(DefFlo(b.defd.id, value)) + b + } + def apply(dir: Direction = null): Flo = + new Flo(dir) +} + +object FloPrimOp { + val FloNeg = PrimOp("flo-neg") + val FloAdd = PrimOp("flo-add") + val FloSub = PrimOp("flo-sub") + val FloMul = PrimOp("flo-mul") + val FloDiv = PrimOp("flo-div") + val FloMod = PrimOp("flo-mod") + val FloEqual = PrimOp("flo-equal") + val FloNotEqual = PrimOp("flo-not-equal") + val FloGreater = PrimOp("flo-greater") + val FloLess = PrimOp("flo-less") + val FloLessEqual = PrimOp("flo-less-equal") + val FloGreaterEqual = PrimOp("flo-greater-equal") + val FloPow = PrimOp("flo-pow") + val FloSin = PrimOp("flo-sin") + val FloCos = PrimOp("flo-cos") + val FloTan = PrimOp("flo-tan") + val FloAsin = PrimOp("flo-asin") + val FloAcos = PrimOp("flo-acos") + val FloAtan = PrimOp("flo-atan") + val FloSqrt = PrimOp("flo-sqrt") + val FloFloor = PrimOp("flo-floor") + val FloCeil = PrimOp("flo-ceil") + val FloRound = PrimOp("flo-round") + val FloLog = PrimOp("flo-log") + val FloToBits = PrimOp("flo-to-bits") + val BitsToFlo = PrimOp("bits-to-flo") +} +import FloPrimOp._ + +class Flo(dir: Direction = NO_DIR) extends Element(dir, 32) with Num[Flo] { + type T = Flo; + def cloneTypeWidth(width: Int): this.type = cloneType + override def fromBits(n: Bits): this.type = { + val d = cloneType + pushCommand(DefPrim(d.defd.id, d.toType, BitsToFlo, Array(this.ref), NoLits)) + d + } + override def toBits: Bits = { + val d = new UInt(dir, 32) + pushCommand(DefPrim(d.defd.id, d.toType, FloToBits, Array(this.ref), NoLits)) + d + } + def toType: Type = FloType(isFlip) + def cloneType: this.type = new Flo(dir).asInstanceOf[this.type] + def flatten: Array[Bits] = Array[Bits](toBits) + + def fromInt(x: Int): Flo = + Flo(x.toFloat).asInstanceOf[this.type] + + private def flo_unop(op: PrimOp): Flo = { + val d = cloneType + pushCommand(DefPrim(d.defd.id, d.toType, op, Array(this.ref), NoLits)) + d + } + private def flo_binop(op: PrimOp, other: Flo): Flo = { + val d = cloneType + pushCommand(DefPrim(d.defd.id, d.toType, op, Array(this.ref, other.ref), NoLits)) + d + } + private def flo_compop(op: PrimOp, other: Flo): Bool = { + val d = new Bool(dir) + pushCommand(DefPrim(d.defd.id, d.toType, op, Array(this.ref, other.ref), NoLits)) + d + } + + def unary_-() = flo_unop(FloNeg) + def + (b: Flo) = flo_binop(FloAdd, b) + def - (b: Flo) = flo_binop(FloSub, b) + def * (b: Flo) = flo_binop(FloMul, b) + def / (b: Flo) = flo_binop(FloDiv, b) + def % (b: Flo) = flo_binop(FloMod, b) + def ===(b: Flo) = flo_compop(FloEqual, b) + def != (b: Flo) = flo_compop(FloNotEqual, b) + def > (b: Flo) = flo_compop(FloGreater, b) + def < (b: Flo) = flo_compop(FloLess, b) + def <= (b: Flo) = flo_compop(FloLessEqual, b) + def >= (b: Flo) = flo_compop(FloGreaterEqual, b) + def pow (b: Flo) = flo_binop(FloPow, b) + def sin = flo_unop(FloSin) + def cos = flo_unop(FloCos) + def tan = flo_unop(FloTan) + def asin = flo_unop(FloAsin) + def acos = flo_unop(FloAcos) + def atan = flo_unop(FloAtan) + def sqrt = flo_unop(FloSqrt) + def floor = flo_unop(FloFloor) + def ceil = flo_unop(FloCeil) + def round = flo_unop(FloRound) + def log = flo_unop(FloLog) + def toSInt () = SInt(OUTPUT).fromBits(toBits) + def toUInt () = UInt(OUTPUT).fromBits(toBits) +} + +/// DBL + +import java.lang.Double.doubleToLongBits + +object Dbl { + def apply(x: Float): Dbl = Dbl(x.toDouble); + def apply(x: Double): Dbl = dblLit(x) + def dblLit(value: Double): Dbl = { + val b = new Dbl(NO_DIR) + pushCommand(DefDbl(b.defd.id, value)) + b + } + def apply(dir: Direction = NO_DIR): Dbl = + new Dbl(dir) +} + +object DblPrimOp { + val DblNeg = PrimOp("dbl-neg") + val DblAdd = PrimOp("dbl-add") + val DblSub = PrimOp("dbl-sub") + val DblMul = PrimOp("dbl-mul") + val DblDiv = PrimOp("dbl-div") + val DblMod = PrimOp("dbl-mod") + val DblEqual = PrimOp("dbl-equal") + val DblNotEqual = PrimOp("dbl-not-equal") + val DblGreater = PrimOp("dbl-greater") + val DblLess = PrimOp("dbl-less") + val DblLessEqual = PrimOp("dbl-less-equal") + val DblGreaterEqual = PrimOp("dbl-greater-equal") + val DblPow = PrimOp("dbl-pow") + val DblSin = PrimOp("dbl-sin") + val DblCos = PrimOp("dbl-cos") + val DblTan = PrimOp("dbl-tan") + val DblAsin = PrimOp("dbl-asin") + val DblAcos = PrimOp("dbl-acos") + val DblAtan = PrimOp("dbl-atan") + val DblSqrt = PrimOp("dbl-sqrt") + val DblFloor = PrimOp("dbl-floor") + val DblCeil = PrimOp("dbl-ceil") + val DblRound = PrimOp("dbl-round") + val DblLog = PrimOp("dbl-log") + val DblToBits = PrimOp("dbl-to-bits") + val BitsToDbl = PrimOp("bits-to-dbl") +} +import DblPrimOp._ + +class Dbl(dir: Direction = null) extends Element(dir, 64) with Num[Dbl] { + // setIsSigned + + // override def setIsTypeNode = {inputs(0).setIsSigned; super.setIsTypeNode} + + type T = Dbl; + def cloneTypeWidth(width: Int): this.type = cloneType + override def fromBits(n: Bits): this.type = { + val d = cloneType + pushCommand(DefPrim(d.defd.id, d.toType, BitsToDbl, Array(this.ref), NoLits)) + d + } + override def toBits: Bits = { + val d = new UInt(dir, 64) + pushCommand(DefPrim(d.defd.id, d.toType, DblToBits, Array(this.ref), NoLits)) + d + } + def toType: Type = DblType(isFlip) + def cloneType: this.type = new Dbl(dir).asInstanceOf[this.type] + def flatten: Array[Bits] = Array[Bits](toBits) + + def fromInt(x: Int): this.type = + Dbl(x.toDouble).asInstanceOf[this.type] + + private def dbl_unop(op: PrimOp): Dbl = { + val d = cloneType + pushCommand(DefPrim(d.defd.id, d.toType, op, Array(this.ref), NoLits)) + d + } + private def dbl_binop(op: PrimOp, other: Dbl): Dbl = { + val d = cloneType + pushCommand(DefPrim(d.defd.id, d.toType, op, Array(this.ref, other.ref), NoLits)) + d + } + private def dbl_compop(op: PrimOp, other: Dbl): Bool = { + val d = new Bool(dir) + pushCommand(DefPrim(d.defd.id, d.toType, op, Array(this.ref, other.ref), NoLits)) + d + } + + def unary_-() = dbl_unop(DblNeg) + def + (b: Dbl) = dbl_binop(DblAdd, b) + def - (b: Dbl) = dbl_binop(DblSub, b) + def * (b: Dbl) = dbl_binop(DblMul, b) + def / (b: Dbl) = dbl_binop(DblDiv, b) + def % (b: Dbl) = dbl_binop(DblMod, b) + def ===(b: Dbl) = dbl_compop(DblEqual, b) + def != (b: Dbl) = dbl_compop(DblNotEqual, b) + def > (b: Dbl) = dbl_compop(DblGreater, b) + def < (b: Dbl) = dbl_compop(DblLess, b) + def <= (b: Dbl) = dbl_compop(DblLessEqual, b) + def >= (b: Dbl) = dbl_compop(DblGreaterEqual, b) + def pow (b: Dbl) = dbl_binop(DblPow, b) + def sin = dbl_unop(DblSin) + def cos = dbl_unop(DblCos) + def tan = dbl_unop(DblTan) + def asin = dbl_unop(DblAsin) + def acos = dbl_unop(DblAcos) + def atan = dbl_unop(DblAtan) + def sqrt = dbl_unop(DblSqrt) + def floor = dbl_unop(DblFloor) + def ceil = dbl_unop(DblCeil) + def round = dbl_unop(DblRound) + def log = dbl_unop(DblLog) + def toSInt () = SInt(OUTPUT).fromBits(toBits) + def toUInt () = UInt(OUTPUT).fromBits(toBits) +} + +object Sin { + def apply (x: Flo): Flo = x.sin + def apply (x: Dbl): Dbl = x.sin +} + +object Cos { + def apply (x: Flo): Flo = x.cos + def apply (x: Dbl): Dbl = x.cos +} + +object Tan { + def apply (x: Flo): Flo = x.tan + def apply (x: Dbl): Dbl = x.tan +} + +object ASin { + def apply (x: Flo): Flo = x.asin + def apply (x: Dbl): Dbl = x.asin +} + +object ACos { + def apply (x: Flo): Flo = x.acos + def apply (x: Dbl): Dbl = x.acos +} + +object ATan { + def apply (x: Flo): Flo = x.atan + def apply (x: Dbl): Dbl = x.atan +} + +object Sqrt { + def apply (x: Flo): Flo = x.sqrt + def apply (x: Dbl): Dbl = x.sqrt +} + +object Floor { + def apply (x: Flo): Flo = x.floor + def apply (x: Dbl): Dbl = x.floor +} + +object Ceil { + def apply (x: Flo): Flo = x.ceil + def apply (x: Dbl): Dbl = x.ceil +} + +object Round { + def apply (x: Flo): Flo = x.round + def apply (x: Dbl): Dbl = x.round +} + +object Log { + def apply (x: Flo): Flo = x.log + def apply (x: Dbl): Dbl = x.log + def apply (x: Flo, p: Flo): Flo = Log(x)/Log(p) + def apply (x: Dbl, p: Dbl): Dbl = Log(x)/Log(p) +} + +object Pow { + def apply (x: Flo, y: Flo): Flo = x.pow(y) + def apply (x: Dbl, y: Dbl): Dbl = x.pow(y) +} diff --git a/src/main/scala/Literal.scala b/src/main/scala/Literal.scala new file mode 100644 index 00000000..fff2780b --- /dev/null +++ b/src/main/scala/Literal.scala @@ -0,0 +1,199 @@ +package Chisel +import scala.math.log +import scala.math.abs +import scala.math.ceil +import scala.math.max +import scala.math.min +import Literal._ +import ChiselError._ + +/* Factory for literal values to be used by Bits and SInt factories. */ +object Lit { + def apply[T <: Bits](n: String)(gen: => T): T = { + makeLit(Literal(n, -1))(gen) + } + + def apply[T <: Bits](n: String, width: Int)(gen: => T): T = { + makeLit(Literal(n, width))(gen) + } + + def apply[T <: Bits](n: String, base: Char)(gen: => T): T = { + makeLit(Literal(-1, base, n))(gen) + } + + def apply[T <: Bits](n: String, base: Char, width: Int)(gen: => T): T = { + makeLit(Literal(width, base, n))(gen) + } + + def apply[T <: Bits](n: BigInt)(gen: => T): T = { + makeLit(Literal(n, signed = gen.isInstanceOf[SInt]))(gen) + } + + def apply[T <: Bits](n: BigInt, width: Int)(gen: => T): T = { + val lit = Literal(n, width, signed = gen.isInstanceOf[SInt]) + makeLit(lit)(gen) + } + + def makeLit[T <: Bits](x: Literal)(gen: => T): T = { + gen.makeLit(x.value, x.width) + } +} + +class Literal(val value: BigInt, val width: Int) { } + +object Literal { + + private def bigMax(x: BigInt, y: BigInt): BigInt = if (x > y) x else y; + def sizeof(x: BigInt): Int = { + val y = bigMax(BigInt(1), x.abs).toDouble; + val res = max(1, (ceil(log(y + 1)/log(2.0))).toInt); + res + } + + private def sizeof(base: Char, x: String): Int = { + var res = 0; + var first = true; + val size = + if(base == 'b') { + 1 + } else if(base == 'h') { + 4 + } else if(base == 'o') { + 3 + } else { + -1 + } + for(c <- x) + if (c == '_') { + + } else if(first) { + first = false; + res += sizeof(c.asDigit); + } else if (c != '_') { + res += size; + } + res + } + val hexNibbles = "0123456789abcdef"; + def toHexNibble(x: String, off: Int): Char = { + var res = 0; + for (i <- 0 until 4) { + val idx = off + i; + val c = if (idx < 0) '0' else x(idx); + res = 2 * res + (if (c == '1') 1 else 0); + } + hexNibbles(res) + } + val pads = Vector(0, 3, 2, 1); + def toHex(x: String): String = { + var res = ""; + val numNibbles = (x.length-1) / 4 + 1; + val pad = pads(x.length % 4); + for (i <- 0 until numNibbles) { + res += toHexNibble(x, i*4 - pad); + } + res + } + def toLitVal(x: String): BigInt = { + BigInt(x.substring(2, x.length), 16) + } + + def toLitVal(x: String, shamt: Int): BigInt = { + var res = BigInt(0); + for(c <- x) + if(c != '_'){ + if(!(hexNibbles + "?").contains(c.toLower)) ChiselError.error({"Literal: " + x + " contains illegal character: " + c}); + res = res * shamt + c.asDigit; + } + res + } + + def removeUnderscore(x: String): String = { + var res = "" + for(c <- x){ + if(c != '_'){ + res = res + c + } + } + res + } + + def parseLit(x: String): (String, String, Int) = { + var bits = ""; + var mask = ""; + var width = 0; + for (d <- x) { + if (d != '_') { + if(!"01?".contains(d)) ChiselError.error({"Literal: " + x + " contains illegal character: " + d}); + width += 1; + mask = mask + (if (d == '?') "0" else "1"); + bits = bits + (if (d == '?') "0" else d.toString); + } + } + (bits, mask, width) + } + def stringToVal(base: Char, x: String): BigInt = { + if(base == 'x') { + toLitVal(x, 16); + } else if(base == 'd') { + BigInt(x.toInt) + } else if(base == 'h') { + toLitVal(x, 16) + } else if(base == 'b') { + toLitVal(x, 2) + } else if(base == 'o') { + toLitVal(x, 8) + } else { + BigInt(-1) + } + } + + /** Derive the bit length for a Literal + * + */ + def bitLength(b: BigInt): Int = { + // Check for signedness + // We have seen unexpected values (one too small) when using .bitLength on negative BigInts, + // so use the positive value instead. + val usePositiveValueForBitLength = false + (if (usePositiveValueForBitLength && b < 0) { + -b + } else { + b + }).bitLength + } + /** Creates a *Literal* instance from a scala integer. + */ + def apply(x: BigInt, width: Int = -1, signed: Boolean = false): Literal = { + // Check for signedness + // We get unexpected values (one too small) when using .bitLength on negative BigInts, + // so use the positive value instead. + val bl = bitLength(x) + val xWidth = if (signed) { + bl + 1 + } else { + max(bl, 1) + } + val w = if(width == -1) xWidth else width + val xString = (if (x >= 0) x else (BigInt(1) << w) + x).toString(16) + + if(xWidth > width && width != -1) { + // Is this a zero-width wire with value 0 + if (!(x == 0 && width == 0 && Driver.isSupportW0W)) { + ChiselError.error({"width " + width + " is too small for literal " + x + ". Smallest allowed width is " + xWidth}); + } + } + apply("h" + xString, w) + } + def apply(n: String, width: Int): Literal = + apply(width, n(0), n.substring(1, n.length)); + + def apply(width: Int, base: Char, literal: String): Literal = { + if (!"dhbo".contains(base)) { + ChiselError.error("no base specified"); + } + new Literal(stringToVal(base, literal), width) + } +} + + diff --git a/src/main/scala/Tester.scala b/src/main/scala/Tester.scala new file mode 100644 index 00000000..fc55fc53 --- /dev/null +++ b/src/main/scala/Tester.scala @@ -0,0 +1,389 @@ +/* + Copyright (c) 2011, 2012, 2013, 2014 The Regents of the University of + California (Regents). All Rights Reserved. Redistribution and use in + source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above + copyright notice, this list of conditions and the following + two paragraphs of disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + two paragraphs of disclaimer in the documentation and/or other materials + provided with the distribution. + * Neither the name of the Regents nor the names of its contributors + may be used to endorse or promote products derived from this + software without specific prior written permission. + + IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, + SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, + ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF + REGENTS HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF + ANY, PROVIDED HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION + TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR + MODIFICATIONS. +*/ + +package Chisel +import Chisel._ +import scala.math._ +import scala.collection.mutable.ArrayBuffer +import scala.collection.mutable.HashMap +import scala.util.Random +import java.io.{File, IOException, InputStream, OutputStream, PrintStream} +import scala.sys.process._ +import scala.io.Source._ +import Literal._ + +case class Poke(val node: Data, val index: Int, val value: BigInt); + +class Snapshot(val t: Int) { + val pokes = new ArrayBuffer[Poke]() +} + +class ManualTester[+T <: Module] + (val c: T, val isT: Boolean = true) { + var testIn: InputStream = null + var testOut: OutputStream = null + var testErr: InputStream = null + val sb = new StringBuilder() + var delta = 0 + var t = 0 + var isTrace = isT + + /** + * Waits until the emulator streams are ready. This is a dirty hack related + * to the way Process works. TODO: FIXME. + */ + def waitForStreams() = { + var waited = 0 + while (testOut == null || testIn == null || testErr == null) { + Thread.sleep(100) + if (waited % 10 == 0 && waited > 30) { + println("waiting for emulator process treams to be valid ...") + } + } + } + + // TODO: MOVE TO SOMEWHERE COMMON TO BACKEND + def ensureDir(dir: String): String = { + val d = dir + (if (dir == "" || dir(dir.length-1) == '/') "" else "/") + new File(d).mkdirs() + d + } + def createOutputFile(name: String): java.io.FileWriter = { + val baseDir = ensureDir(Driver.targetDir) + new java.io.FileWriter(baseDir + name) + } + + def puts(str: String) = { + while (testOut == null) { Thread.sleep(100) } + for (e <- str) testOut.write(e); + } + + /** + * Sends a command to the emulator and returns the reply. + * The standard protocol treats a single line as a command, which always + * returns a single line of reply. + */ + def emulatorCmd(str: String): String = { + // validate cmd + if (str contains "\n") { + System.err.print(s"emulatorCmd($str): command should not contain newline") + return "error" + } + + waitForStreams() + + // send command to emulator + for (e <- str) testOut.write(e); + testOut.write('\n'); + testOut.flush() + + // read output from emulator + var c = testIn.read + sb.clear() + while (c != '\n' && c != -1) { + if (c == 0) { + Thread.sleep(100) + } + sb += c.toChar + // Look for a "PRINT" command. + if (sb.length == 6 && sb.startsWith("PRINT ")) { + do { + c = testIn.read + sb += c.toChar + } while (c != ' ') + // Get the PRINT character count. + val printCommand = """^PRINT (\d+) """.r + val printCommand(nChars) = sb.toString + sb.clear() + for (i <- 0 until nChars.toInt) { + c = testIn.read + sb += c.toChar + } + System.out.print(sb.toString()) + sb.clear() + } + c = testIn.read + } + + // drain errors + try { + while(testErr.available() > 0) { + System.err.print(Character.toChars(testErr.read())) + } + } catch { + case e : IOException => testErr = null; println("ERR EXCEPTION") + } + + if (sb == "error") { + System.err.print(s"FAILED: emulatorCmd($str): returned error") + ok = false + } + return sb.toString + } + + /* + def setClocks(clocks: HashMap[Clock, Int]) { + var cmd = "set_clocks" + for (clock <- Driver.clocks) { + if (clock.srcClock == null) { + val s = BigInt(clocks(clock)).toString(16) + cmd = cmd + " " + s + } + } + emulatorCmd(cmd) + // TODO: check for errors in return + } + */ + + def doPeekBits(name: String, off: Int = -1): BigInt = { + if (name == "") { + println("Unable to peek data " + name) // TODO: USE DATA + -1 + } else { + var cmd = "" + if (off != -1) { + cmd = "mem_peek " + name + " " + off; + } else { + cmd = "wire_peek " + name; + } + val s = emulatorCmd(cmd) + val rv = toLitVal(s) + if (isTrace) println(" PEEK " + name + " " + (if (off >= 0) (off + " ") else "") + "-> " + s) + rv + } + } + + def peekBits(data: Data, off: Int = -1): BigInt = { + doPeekBits(data.debugName, off) + } + + def signed_fix(dtype: Element, rv: BigInt): BigInt = { + val w = dtype.width + dtype match { + /* Any "signed" node */ + case _: SInt | _ : Flo | _: Dbl => (if(rv >= (BigInt(1) << w - 1)) (rv - (BigInt(1) << w)) else rv) + /* anything else (i.e., UInt) */ + case _ => (rv) + } + } + + def peekAt[T <: Bits](data: Data, off: Int): BigInt = { + // signed_fix(data(1), peekBits(data, off)) + doPeekBits(data.debugName, off) + } + + def peek(data: Bits): BigInt = { + signed_fix(data, peekBits(data)) + } + + def peek(data: Aggregate /*, off: Int = -1 */): Array[BigInt] = { + data.flatten.map(peek(_)) + } + + def reset(n: Int = 1) = { + emulatorCmd("reset " + n) + // TODO: check for errors in return + if (isTrace) println("RESET " + n) + } + + def doPokeBits(name: String, x: BigInt, off: Int): Unit = { + if (name == "") { + println("Unable to poke data " + name) // TODO: data.toString + } else { + + var cmd = "" + if (off != -1) { + cmd = "mem_poke " + name + " " + off; + } else { + cmd = "wire_poke " + name; + } + // Don't prefix negative numbers with "0x" + val radixPrefix = if (x < 0) " -0x" else " 0x" + val xval = radixPrefix + x.abs.toString(16) + cmd = cmd + xval + if (isTrace) { + println(" POKE " + name + " " + (if (off >= 0) (off + " ") else "") + "<- " + xval) + } + val rtn = emulatorCmd(cmd) + if (rtn != "ok") { + System.err.print(s"FAILED: poke(${name}) returned false") + ok = false + } + } + } + + def pokeAt[T <: Bits](data: Data, x: BigInt, off: Int): Unit = { + doPokeBits(data.debugName, x, off) + } + + def pokeBits(data: Data, x: BigInt, off: Int = -1): Unit = { + doPokeBits(data.debugName, x, off) + } + + def poke(data: Bits, x: BigInt): Unit = { + pokeBits(data, x) + } + + def poke(data: Aggregate, x: Array[BigInt]): Unit = { + val kv = (data.flatten, x.reverse).zipped; + for ((x, y) <- kv) + poke(x, y) + } + + def step(n: Int) = { + val target = t + n + val s = emulatorCmd("step " + n) + delta += s.toInt + if (isTrace) println("STEP " + n + " -> " + target) + t += n + } + + def int(x: Boolean): BigInt = if (x) 1 else 0 + def int(x: Int): BigInt = x + def int(x: Bits): BigInt = x.litValue.getOrElse(-1) + + var ok = true; + var failureTime = -1 + + def expect (good: Boolean, msg: String): Boolean = { + if (isTrace) + println(msg + " " + (if (good) "PASS" else "FAIL")) + if (!good) { ok = false; if (failureTime == -1) failureTime = t; } + good + } + + def expect (data: Bits, expected: BigInt): Boolean = { + // val mask = (BigInt(1) << data) - 1 + val got = peek(data) + + // expect((got & mask) == (expected & mask), + expect(got == expected, + "EXPECT " + data.debugName + " <- " + got + " == " + expected) + } + + def expect (data: Aggregate, expected: Array[BigInt]): Boolean = { + val kv = (data.flatten, expected.reverse).zipped; + var allGood = true + for ((d, e) <- kv) + allGood = expect(d, e) && allGood + allGood + } + + /* We need the following so scala doesn't use our "tolerant" Float version of expect. + */ + def expect (data: Bits, expected: Int): Boolean = { + expect(data, BigInt(expected)) + } + def expect (data: Bits, expected: Long): Boolean = { + expect(data, BigInt(expected)) + } + + /* Compare the floating point value of a node with an expected floating point value. + * We will tolerate differences in the bottom bit. + */ + def expect (data: Bits, expected: Float): Boolean = { + val gotBits = peek(data).toInt + val expectedBits = java.lang.Float.floatToIntBits(expected) + var gotFLoat = java.lang.Float.intBitsToFloat(gotBits) + var expectedFloat = expected + if (gotFLoat != expectedFloat) { + val gotDiff = gotBits - expectedBits + // Do we have a single bit difference? + if (abs(gotDiff) <= 1) { + expectedFloat = gotFLoat + } + } + expect(gotFLoat == expectedFloat, + "EXPECT " + data.debugName + " <- " + gotFLoat + " == " + expectedFloat) + } + + val rnd = if (Driver.testerSeedValid) new Random(Driver.testerSeed) else new Random() + var process: Process = null + + def start(): Process = { + val n = Driver.appendString(Some(c.name),Driver.chiselConfigClassName) + val target = Driver.targetDir + "/" + n + val cmd = target + println("OPENING " + cmd) + /* + (if (Driver.backend.isInstanceOf[FloBackend]) { + val dir = Driver.backend.asInstanceOf[FloBackend].floDir + val command = ArrayBuffer(dir + "fix-console", ":is-debug", "true", ":filename", target + ".hex", ":flo-filename", target + ".mwe.flo") + if (Driver.isVCD) { command ++= ArrayBuffer(":is-vcd-dump", "true") } + if (Driver.emitTempNodes) { command ++= ArrayBuffer(":emit-temp-nodes", "true") } + command ++= ArrayBuffer(":target-dir", Driver.targetDir) + command.mkString(" ") + } else { + target + (if (Driver.backend.isInstanceOf[VerilogBackend]) " -q +vcs+initreg+0 " else "") + }) + */ + println("SEED " + Driver.testerSeed) + println("STARTING " + cmd) + val processBuilder = Process(cmd) + val pio = new ProcessIO(in => testOut = in, out => testIn = out, err => testErr = err) + process = processBuilder.run(pio) + waitForStreams() + t = 0 + reset(5) + // Skip vpd message + if (Driver.backend.isInstanceOf[VerilogBackend] && Driver.isDebug) { + var vpdmsg = testIn.read + while (vpdmsg != '\n' && vpdmsg != -1) + vpdmsg = testIn.read + } + process + } + + def finish(): Boolean = { + if (process != null) { + emulatorCmd("quit") + + if (testOut != null) { + testOut.flush() + testOut.close() + } + if (testIn != null) { + testIn.close() + } + if (testErr != null) { + testErr.close() + } + + process.destroy() + } + println("RAN " + t + " CYCLES " + (if (ok) "PASSED" else { "FAILED FIRST AT CYCLE " + failureTime })) + ok + } +} + +class Tester[+T <: Module](c: T, isTrace: Boolean = true) extends ManualTester(c, isTrace) { + start() +} + diff --git a/src/main/scala/utils.scala b/src/main/scala/utils.scala new file mode 100644 index 00000000..346ce657 --- /dev/null +++ b/src/main/scala/utils.scala @@ -0,0 +1,253 @@ +package Chisel +import Builder._ +import scala.math._ + +object log2Up +{ + def apply(in: Int): Int = if(in == 1) 1 else ceil(log(in)/log(2)).toInt +} + +object log2Ceil +{ + def apply(in: Int): Int = ceil(log(in)/log(2)).toInt +} + + +object log2Down +{ + def apply(x : Int): Int = if (x == 1) 1 else floor(log(x)/log(2.0)).toInt +} + +object log2Floor +{ + def apply(x : Int): Int = floor(log(x)/log(2.0)).toInt +} + + +object isPow2 +{ + def apply(in: Int): Boolean = in > 0 && ((in & (in-1)) == 0) +} + +object FillInterleaved +{ + def apply(n: Int, in: Bits): Bits = apply(n, in.toBools) + def apply(n: Int, in: Seq[Bool]): Bits = Vec(in.map(Fill(n, _))).toBits +} + +/** Returns the number of bits set (i.e value is 1) in the input signal. + */ +object PopCount +{ + def apply(in: Iterable[Bool]): UInt = { + if (in.size == 0) { + UInt(0) + } else if (in.size == 1) { + in.head + } else { + apply(in.slice(0, in.size/2)) + Cat(UInt(0), apply(in.slice(in.size/2, in.size))) + } + } + def apply(in: Bits): UInt = apply((0 until in.getWidth).map(in(_))) +} + +object RegNext { + + def apply[T <: Data](next: T): T = Reg[T](next, next, null.asInstanceOf[T]) + + def apply[T <: Data](next: T, init: T): T = Reg[T](next, next, init) + +} + +object RegInit { + + def apply[T <: Data](init: T): T = Reg[T](init, null.asInstanceOf[T], init) + +} + +object RegEnable +{ + def apply[T <: Data](updateData: T, enable: Bool) = { + val r = Reg(updateData) + when (enable) { r := updateData } + r + } + def apply[T <: Data](updateData: T, resetData: T, enable: Bool) = { + val r = RegInit(resetData) + when (enable) { r := updateData } + r + } +} + +/** Builds a Mux tree out of the input signal vector using a one hot encoded + select signal. Returns the output of the Mux tree. + */ +object Mux1H +{ + def apply[T <: Data](sel: Iterable[Bool], in: Iterable[T]): T = { + if (in.tail.isEmpty) in.head + else { + val masked = (sel, in).zipped map ((s, i) => Mux(s, i.toBits, Bits(0))) + in.head.fromBits(masked.reduceLeft(_|_)) + } + } + def apply[T <: Data](in: Iterable[(Bool, T)]): T = { + val (sel, data) = in.unzip + apply(sel, data) + } + def apply[T <: Data](sel: Bits, in: Iterable[T]): T = + apply((0 until in.size).map(sel(_)), in) + def apply(sel: Bits, in: Bits): Bool = (sel & in).orR +} + +/** Builds a Mux tree under the assumption that multiple select signals + can be enabled. Priority is given to the first select signal. + + Returns the output of the Mux tree. + */ +object PriorityMux +{ + def apply[T <: Bits](in: Iterable[(Bool, T)]): T = { + if (in.size == 1) { + in.head._2 + } else { + Mux(in.head._1, in.head._2, apply(in.tail)) + } + } + def apply[T <: Bits](sel: Iterable[Bool], in: Iterable[T]): T = apply(sel zip in) + def apply[T <: Bits](sel: Bits, in: Iterable[T]): T = apply((0 until in.size).map(sel(_)), in) +} + +object unless { + def apply(c: Bool)(block: => Unit) { + when (!c) { block } + } +} + +object switch { + def apply(c: Bits)(block: => Unit) { + switchKeys.push(c) + block + switchKeys.pop() + } +} + +object is { + def apply(v: Bits)(block: => Unit): Unit = + apply(Seq(v))(block) + def apply(v: Bits, vr: Bits*)(block: => Unit): Unit = + apply(v :: vr.toList)(block) + def apply(v: Iterable[Bits])(block: => Unit): Unit = { + val keys = switchKeys + if (keys.isEmpty) ChiselError.error("The 'is' keyword may not be used outside of a switch.") + else if (!v.isEmpty) when (v.map(_ === keys.top).reduce(_||_)) { block } + } +} + +object MuxLookup { + def apply[S <: UInt, T <: Bits] (key: S, default: T, mapping: Seq[(S, T)]): T = { + var res = default; + for ((k, v) <- mapping.reverse) + res = Mux(key === k, v, res); + res + } + +} + +object Fill { + def apply(n: Int, x: Bool): UInt = n match { + case 0 => UInt(width=0) + case 1 => x + case x if n > 1 => UInt(0,n) - UInt(x) + case _ => throw new IllegalArgumentException(s"n (=$n) must be nonnegative integer.") + } + def apply(n: Int, y: UInt): UInt = { + n match { + case 0 => UInt(width=0) + case 1 => y + case x if n > 1 => + val p2 = Array.ofDim[UInt](log2Up(n+1)) + p2(0) = y + for (i <- 1 until p2.length) + p2(i) = Cat(p2(i-1), p2(i-1)) + Cat((0 until log2Up(x+1)).filter(i => (x & (1 << i)) != 0).map(p2(_))) + case _ => throw new IllegalArgumentException(s"n (=$n) must be nonnegative integer.") + } + } +} + +object MuxCase { + def apply[T <: Bits] (default: T, mapping: Seq[(Bool, T)]): T = { + var res = default; + for ((t, v) <- mapping.reverse){ + res = Mux(t, v, res); + } + res + } +} + +object ListLookup { + def apply[T <: Data](addr: UInt, default: List[T], mapping: Array[(UInt, List[T])]): List[T] = { + val map = mapping.map(m => (m._1 === addr, m._2)) + default.zipWithIndex map { case (d, i) => + map.foldRight(d)((m, n) => Mux(m._1, m._2(i), n)) + } + } +} + +object Lookup { + def apply[T <: Bits](addr: UInt, default: T, mapping: Seq[(UInt, T)]): T = + ListLookup(addr, List(default), mapping.map(m => (m._1, List(m._2))).toArray).head +} + +/** Litte/big bit endian convertion: reverse the order of the bits in a UInt. +*/ +object Reverse +{ + private def doit(in: UInt, length: Int): UInt = { + if (length == 1) { + in + } else if (isPow2(length) && length >= 8 && length <= 64) { + // Do it in logarithmic time to speed up C++. Neutral for real HW. + var res = in + var shift = length >> 1 + var mask = UInt((BigInt(1) << length) - 1, length) + do { + mask = mask ^ (mask(length-shift-1,0) << UInt(shift)) + res = ((res >> UInt(shift)) & mask) | (res(length-shift-1,0) << UInt(shift) & ~mask) + shift = shift >> 1 + } while (shift > 0) + res + } else { + val half = (1 << log2Up(length))/2 + Cat(doit(in(half-1,0), half), doit(in(length-1,half), length-half)) + } + } + def apply(in: UInt): UInt = doit(in, in.getWidth) +} + +/** Returns the n-cycle delayed version of the input signal. + */ +object ShiftRegister +{ + def apply[T <: Data](in: T, n: Int, en: Bool = Bool(true)): T = + { + // The order of tests reflects the expected use cases. + if (n == 1) { + RegEnable(in, en) + } else if (n != 0) { + RegNext(apply(in, n-1, en)) + } else { + in + } + } +} + +/** Returns the one hot encoding of the input UInt. + */ +object UIntToOH +{ + def apply(in: UInt, width: Int = -1): UInt = + if (width == -1) UInt(1) << in + else (UInt(1) << in(log2Up(width)-1,0))(width-1,0) +} |
