summaryrefslogtreecommitdiff
path: root/chiselFrontend/src/main/scala/chisel3/internal
diff options
context:
space:
mode:
authorJim Lawson2016-06-20 11:08:46 -0700
committerJim Lawson2016-06-20 11:08:46 -0700
commitd408d73a171535bd7c2ba9d0037c194022b8a62f (patch)
tree81885a99ec56e89532bc3fa338f22b163dcc4d1f /chiselFrontend/src/main/scala/chisel3/internal
parentb5a534914795d9d17f4dfe623525f1b804e4c60f (diff)
Rename chisel3 package.
Diffstat (limited to 'chiselFrontend/src/main/scala/chisel3/internal')
-rw-r--r--chiselFrontend/src/main/scala/chisel3/internal/Builder.scala129
-rw-r--r--chiselFrontend/src/main/scala/chisel3/internal/Error.scala91
-rw-r--r--chiselFrontend/src/main/scala/chisel3/internal/SourceInfo.scala51
-rw-r--r--chiselFrontend/src/main/scala/chisel3/internal/firrtl/IR.scala190
4 files changed, 461 insertions, 0 deletions
diff --git a/chiselFrontend/src/main/scala/chisel3/internal/Builder.scala b/chiselFrontend/src/main/scala/chisel3/internal/Builder.scala
new file mode 100644
index 00000000..01628105
--- /dev/null
+++ b/chiselFrontend/src/main/scala/chisel3/internal/Builder.scala
@@ -0,0 +1,129 @@
+// See LICENSE for license details.
+
+package chisel.internal
+
+import scala.util.DynamicVariable
+import scala.collection.mutable.{ArrayBuffer, HashMap}
+
+import chisel._
+import core._
+import firrtl._
+
+private[chisel] class Namespace(parent: Option[Namespace], keywords: Set[String]) {
+ private val names = collection.mutable.HashMap[String, Long]()
+ for (keyword <- keywords)
+ names(keyword) = 1
+
+ private def rename(n: String): String = {
+ val index = names.getOrElse(n, 1L)
+ val tryName = s"${n}_${index}"
+ names(n) = index + 1
+ if (this contains tryName) rename(n) else tryName
+ }
+
+ def contains(elem: String): Boolean = {
+ names.contains(elem) || parent.map(_ contains elem).getOrElse(false)
+ }
+
+ def name(elem: String): String = {
+ if (this contains elem) {
+ name(rename(elem))
+ } else {
+ names(elem) = 1
+ elem
+ }
+ }
+
+ def child(kws: Set[String]): Namespace = new Namespace(Some(this), kws)
+ def child: Namespace = child(Set())
+}
+
+private[chisel] class IdGen {
+ private var counter = -1L
+ def next: Long = {
+ counter += 1
+ counter
+ }
+}
+
+private[chisel] trait HasId {
+ private[chisel] def _onModuleClose {} // scalastyle:ignore method.name
+ private[chisel] val _parent = Builder.dynamicContext.currentModule
+ _parent.foreach(_.addId(this))
+
+ private[chisel] val _id = Builder.idGen.next
+ override def hashCode: Int = _id.toInt
+ override def equals(that: Any): Boolean = that match {
+ case x: HasId => _id == x._id
+ case _ => false
+ }
+
+ // Facilities for 'suggesting' a name to this.
+ // Post-name hooks called to carry the suggestion to other candidates as needed
+ private var suggested_name: Option[String] = None
+ private val postname_hooks = scala.collection.mutable.ListBuffer.empty[String=>Unit]
+ // Only takes the first suggestion!
+ def suggestName(name: =>String): this.type = {
+ if(suggested_name.isEmpty) suggested_name = Some(name)
+ for(hook <- postname_hooks) { hook(name) }
+ this
+ }
+ private[chisel] def addPostnameHook(hook: String=>Unit): Unit = postname_hooks += hook
+
+ // Uses a namespace to convert suggestion into a true name
+ // Will not do any naming if the reference already assigned.
+ // (e.g. tried to suggest a name to part of a Bundle)
+ private[chisel] def forceName(default: =>String, namespace: Namespace): Unit =
+ if(_ref.isEmpty) {
+ val candidate_name = suggested_name.getOrElse(default)
+ val available_name = namespace.name(candidate_name)
+ setRef(Ref(available_name))
+ }
+
+ private var _ref: Option[Arg] = None
+ private[chisel] def setRef(imm: Arg): Unit = _ref = Some(imm)
+ private[chisel] def setRef(parent: HasId, name: String): Unit = setRef(Slot(Node(parent), name))
+ private[chisel] def setRef(parent: HasId, index: Int): Unit = setRef(Index(Node(parent), ILit(index)))
+ private[chisel] def setRef(parent: HasId, index: UInt): Unit = setRef(Index(Node(parent), index.ref))
+ private[chisel] def getRef: Arg = _ref.get
+}
+
+private[chisel] class DynamicContext {
+ val idGen = new IdGen
+ val globalNamespace = new Namespace(None, Set())
+ val components = ArrayBuffer[Component]()
+ var currentModule: Option[Module] = None
+ val errors = new ErrorLog
+}
+
+private[chisel] object Builder {
+ // All global mutable state must be referenced via dynamicContextVar!!
+ private val dynamicContextVar = new DynamicVariable[Option[DynamicContext]](None)
+
+ def dynamicContext: DynamicContext =
+ dynamicContextVar.value getOrElse (new DynamicContext)
+ def idGen: IdGen = dynamicContext.idGen
+ def globalNamespace: Namespace = dynamicContext.globalNamespace
+ def components: ArrayBuffer[Component] = dynamicContext.components
+
+ def pushCommand[T <: Command](c: T): T = {
+ dynamicContext.currentModule.foreach(_._commands += c)
+ c
+ }
+ def pushOp[T <: Data](cmd: DefPrim[T]): T = pushCommand(cmd).id
+
+ def errors: ErrorLog = dynamicContext.errors
+ def error(m: => String): Unit = errors.error(m)
+
+ def build[T <: Module](f: => T): Circuit = {
+ dynamicContextVar.withValue(Some(new DynamicContext)) {
+ errors.info("Elaborating design...")
+ val mod = f
+ mod.forceName(mod.name, globalNamespace)
+ errors.checkpoint()
+ errors.info("Done elaborating.")
+
+ Circuit(components.last.name, components)
+ }
+ }
+}
diff --git a/chiselFrontend/src/main/scala/chisel3/internal/Error.scala b/chiselFrontend/src/main/scala/chisel3/internal/Error.scala
new file mode 100644
index 00000000..f0481dc4
--- /dev/null
+++ b/chiselFrontend/src/main/scala/chisel3/internal/Error.scala
@@ -0,0 +1,91 @@
+// See LICENSE for license details.
+
+package chisel.internal
+
+import scala.collection.mutable.ArrayBuffer
+
+import chisel.core._
+
+class ChiselException(message: String, cause: Throwable) extends Exception(message, cause)
+
+private[chisel] object throwException {
+ def apply(s: String, t: Throwable = null): Nothing =
+ throw new ChiselException(s, t)
+}
+
+/** Records and reports runtime errors and warnings. */
+private[chisel] class ErrorLog {
+ def hasErrors: Boolean = errors.exists(_.isFatal)
+
+ /** Log an error message */
+ def error(m: => String): Unit =
+ errors += new Error(m, getUserLineNumber)
+
+ /** Log a warning message */
+ def warning(m: => String): Unit =
+ errors += new Warning(m, getUserLineNumber)
+
+ /** Emit an informational message */
+ def info(m: String): Unit =
+ println(new Info("[%2.3f] %s".format(elapsedTime/1e3, m), None)) // scalastyle:ignore regex
+
+ /** Prints error messages generated by Chisel at runtime. */
+ def report(): Unit = errors foreach println // scalastyle:ignore regex
+
+ /** Throw an exception if any errors have yet occurred. */
+ def checkpoint(): Unit = if(hasErrors) {
+ import Console._
+ throwException(errors.map(_ + "\n").reduce(_ + _) +
+ UNDERLINED + "CODE HAS " + errors.filter(_.isFatal).length + RESET +
+ UNDERLINED + " " + RED + "ERRORS" + RESET +
+ UNDERLINED + " and " + errors.filterNot(_.isFatal).length + RESET +
+ UNDERLINED + " " + YELLOW + "WARNINGS" + RESET)
+ }
+
+ private def findFirstUserFrame(stack: Array[StackTraceElement]): Option[StackTraceElement] = {
+ def isUserCode(ste: StackTraceElement): Boolean = {
+ def isUserModule(c: Class[_]): Boolean =
+ c != null && (c == classOf[Module] || isUserModule(c.getSuperclass))
+ isUserModule(Class.forName(ste.getClassName))
+ }
+
+ stack.indexWhere(isUserCode) match {
+ case x if x < 0 => None
+ case x => Some(stack(x))
+ }
+ }
+
+ private def getUserLineNumber =
+ findFirstUserFrame(Thread.currentThread().getStackTrace)
+
+ private val errors = ArrayBuffer[LogEntry]()
+
+ private val startTime = System.currentTimeMillis
+ private def elapsedTime: Long = System.currentTimeMillis - startTime
+}
+
+private abstract class LogEntry(msg: => String, line: Option[StackTraceElement]) {
+ def isFatal: Boolean = false
+ def format: String
+
+ override def toString: String = line match {
+ case Some(l) => s"${format} ${l.getFileName}:${l.getLineNumber}: ${msg} in class ${l.getClassName}"
+ case None => s"${format} ${msg}"
+ }
+
+ protected def tag(name: String, color: String): String =
+ s"[${color}${name}${Console.RESET}]"
+}
+
+private class Error(msg: => String, line: Option[StackTraceElement]) extends LogEntry(msg, line) {
+ override def isFatal: Boolean = true
+ def format: String = tag("error", Console.RED)
+}
+
+private class Warning(msg: => String, line: Option[StackTraceElement]) extends LogEntry(msg, line) {
+ def format: String = tag("warn", Console.YELLOW)
+}
+
+private class Info(msg: => String, line: Option[StackTraceElement]) extends LogEntry(msg, line) {
+ def format: String = tag("info", Console.MAGENTA)
+}
diff --git a/chiselFrontend/src/main/scala/chisel3/internal/SourceInfo.scala b/chiselFrontend/src/main/scala/chisel3/internal/SourceInfo.scala
new file mode 100644
index 00000000..c20bd130
--- /dev/null
+++ b/chiselFrontend/src/main/scala/chisel3/internal/SourceInfo.scala
@@ -0,0 +1,51 @@
+// See LICENSE for license details.
+
+// This file contains macros for adding source locators at the point of invocation.
+//
+// This is not part of coreMacros to disallow this macro from being implicitly invoked in Chisel
+// frontend (and generating source locators in Chisel core), which is almost certainly a bug.
+//
+// Note: While these functions and definitions are not private (macros can't be
+// private), these are NOT meant to be part of the public API (yet) and no
+// forward compatibility guarantees are made.
+// A future revision may stabilize the source locator API to allow library
+// writers to append source locator information at the point of a library
+// function invocation.
+
+package chisel.internal.sourceinfo
+
+import scala.language.experimental.macros
+import scala.reflect.macros.blackbox.Context
+
+/** Abstract base class for generalized source information.
+ */
+sealed trait SourceInfo
+
+sealed trait NoSourceInfo extends SourceInfo
+
+/** For when source info can't be generated because of a technical limitation, like for Reg because
+ * Scala macros don't support named or default arguments.
+ */
+case object UnlocatableSourceInfo extends NoSourceInfo
+
+/** For when source info isn't generated because the function is deprecated and we're lazy.
+ */
+case object DeprecatedSourceInfo extends NoSourceInfo
+
+/** For FIRRTL lines from a Scala source line.
+ */
+case class SourceLine(filename: String, line: Int, col: Int) extends SourceInfo
+
+/** Provides a macro that returns the source information at the invocation point.
+ */
+object SourceInfoMacro {
+ def generate_source_info(c: Context): c.Tree = {
+ import c.universe._
+ val p = c.enclosingPosition
+ q"_root_.chisel.internal.sourceinfo.SourceLine(${p.source.file.name}, ${p.line}, ${p.column})"
+ }
+}
+
+object SourceInfo {
+ implicit def materialize: SourceInfo = macro SourceInfoMacro.generate_source_info
+}
diff --git a/chiselFrontend/src/main/scala/chisel3/internal/firrtl/IR.scala b/chiselFrontend/src/main/scala/chisel3/internal/firrtl/IR.scala
new file mode 100644
index 00000000..70e9938b
--- /dev/null
+++ b/chiselFrontend/src/main/scala/chisel3/internal/firrtl/IR.scala
@@ -0,0 +1,190 @@
+// See LICENSE for license details.
+
+package chisel.internal.firrtl
+
+import chisel._
+import core._
+import chisel.internal._
+import chisel.internal.sourceinfo.{SourceInfo, NoSourceInfo}
+
+case class PrimOp(val name: String) {
+ override def toString: String = name
+}
+
+object PrimOp {
+ val AddOp = PrimOp("add")
+ val SubOp = PrimOp("sub")
+ val TailOp = PrimOp("tail")
+ val HeadOp = PrimOp("head")
+ val TimesOp = PrimOp("mul")
+ val DivideOp = PrimOp("div")
+ val RemOp = PrimOp("rem")
+ val ShiftLeftOp = PrimOp("shl")
+ val ShiftRightOp = PrimOp("shr")
+ val DynamicShiftLeftOp = PrimOp("dshl")
+ val DynamicShiftRightOp = PrimOp("dshr")
+ val BitAndOp = PrimOp("and")
+ val BitOrOp = PrimOp("or")
+ val BitXorOp = PrimOp("xor")
+ val BitNotOp = PrimOp("not")
+ val ConcatOp = PrimOp("cat")
+ 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 PadOp = PrimOp("pad")
+ val NotEqualOp = PrimOp("neq")
+ val NegOp = PrimOp("neg")
+ val MultiplexOp = PrimOp("mux")
+ val XorReduceOp = PrimOp("xorr")
+ val ConvertOp = PrimOp("cvt")
+ val AsUIntOp = PrimOp("asUInt")
+ val AsSIntOp = PrimOp("asSInt")
+}
+
+abstract class Arg {
+ def fullName(ctx: Component): String = name
+ def name: String
+}
+
+case class Node(id: HasId) extends Arg {
+ override def fullName(ctx: Component): String = id.getRef.fullName(ctx)
+ def name: String = id.getRef.name
+}
+
+abstract class LitArg(val num: BigInt, widthArg: Width) extends Arg {
+ private[chisel] def forcedWidth = widthArg.known
+ private[chisel] def width: Width = if (forcedWidth) widthArg else Width(minWidth)
+
+ protected def minWidth: Int
+ if (forcedWidth) {
+ require(widthArg.get >= minWidth,
+ s"The literal value ${num} was elaborated with a specificed width of ${widthArg.get} bits, but at least ${minWidth} bits are required.")
+ }
+}
+
+case class ILit(n: BigInt) extends Arg {
+ def name: String = n.toString
+}
+
+case class ULit(n: BigInt, w: Width) extends LitArg(n, w) {
+ def name: String = "UInt" + width + "(\"h0" + num.toString(16) + "\")"
+ def minWidth: Int = 1 max n.bitLength
+
+ require(n >= 0, s"UInt literal ${n} is negative")
+}
+
+case class SLit(n: BigInt, w: Width) extends LitArg(n, w) {
+ def name: String = {
+ val unsigned = if (n < 0) (BigInt(1) << width.get) + n else n
+ s"asSInt(${ULit(unsigned, width).name})"
+ }
+ def minWidth: Int = 1 + n.bitLength
+}
+
+case class Ref(name: String) extends Arg
+case class ModuleIO(mod: Module, name: String) extends Arg {
+ override def fullName(ctx: Component): String =
+ if (mod eq ctx.id) name else s"${mod.getRef.name}.$name"
+}
+case class Slot(imm: Node, name: String) extends Arg {
+ override def fullName(ctx: Component): String =
+ if (imm.fullName(ctx).isEmpty) name else s"${imm.fullName(ctx)}.${name}"
+}
+case class Index(imm: Arg, value: Arg) extends Arg {
+ def name: String = s"[$value]"
+ override def fullName(ctx: Component): String = s"${imm.fullName(ctx)}[${value.fullName(ctx)}]"
+}
+
+object Width {
+ def apply(x: Int): Width = KnownWidth(x)
+ def apply(): Width = UnknownWidth()
+}
+
+sealed abstract class Width {
+ type W = Int
+ def max(that: Width): Width = this.op(that, _ max _)
+ def + (that: Width): Width = this.op(that, _ + _)
+ def + (that: Int): Width = this.op(this, (a, b) => a + that)
+ def shiftRight(that: Int): Width = this.op(this, (a, b) => 0 max (a - that))
+ def dynamicShiftLeft(that: Width): Width =
+ this.op(that, (a, b) => a + (1 << b) - 1)
+
+ def known: Boolean
+ def get: W
+ protected def op(that: Width, f: (W, W) => W): Width
+}
+
+sealed case class UnknownWidth() extends Width {
+ def known: Boolean = false
+ def get: Int = None.get
+ def op(that: Width, f: (W, W) => W): Width = this
+ override def toString: String = ""
+}
+
+sealed case class KnownWidth(value: Int) extends Width {
+ require(value >= 0)
+ def known: Boolean = true
+ def get: Int = value
+ def op(that: Width, f: (W, W) => W): Width = that match {
+ case KnownWidth(x) => KnownWidth(f(value, x))
+ case _ => that
+ }
+ override def toString: String = s"<${value.toString}>"
+}
+
+sealed abstract class MemPortDirection(name: String) {
+ override def toString: String = name
+}
+object MemPortDirection {
+ object READ extends MemPortDirection("read")
+ object WRITE extends MemPortDirection("write")
+ object RDWR extends MemPortDirection("rdwr")
+ object INFER extends MemPortDirection("infer")
+}
+
+abstract class Command {
+ def sourceInfo: SourceInfo
+}
+abstract class Definition extends Command {
+ def id: HasId
+ def name: String = id.getRef.name
+}
+case class DefPrim[T <: Data](sourceInfo: SourceInfo, id: T, op: PrimOp, args: Arg*) extends Definition
+case class DefInvalid(sourceInfo: SourceInfo, arg: Arg) extends Command
+case class DefWire(sourceInfo: SourceInfo, id: Data) extends Definition
+case class DefReg(sourceInfo: SourceInfo, id: Data, clock: Arg) extends Definition
+case class DefRegInit(sourceInfo: SourceInfo, id: Data, clock: Arg, reset: Arg, init: Arg) extends Definition
+case class DefMemory(sourceInfo: SourceInfo, id: HasId, t: Data, size: Int) extends Definition
+case class DefSeqMemory(sourceInfo: SourceInfo, id: HasId, t: Data, size: Int) extends Definition
+case class DefMemPort[T <: Data](sourceInfo: SourceInfo, id: T, source: Node, dir: MemPortDirection, index: Arg, clock: Arg) extends Definition
+case class DefInstance(sourceInfo: SourceInfo, id: Module, ports: Seq[Port]) extends Definition
+case class WhenBegin(sourceInfo: SourceInfo, pred: Arg) extends Command
+case class WhenEnd(sourceInfo: SourceInfo) extends Command
+case class Connect(sourceInfo: SourceInfo, loc: Node, exp: Arg) extends Command
+case class BulkConnect(sourceInfo: SourceInfo, loc1: Node, loc2: Node) extends Command
+case class ConnectInit(sourceInfo: SourceInfo, loc: Node, exp: Arg) extends Command
+case class Stop(sourceInfo: SourceInfo, clk: Arg, ret: Int) extends Command
+case class Component(id: Module, name: String, ports: Seq[Port], commands: Seq[Command]) extends Arg
+case class Port(id: Data, dir: Direction)
+case class Printf(sourceInfo: SourceInfo, clk: Arg, formatIn: String, ids: Seq[Arg]) extends Command {
+ require(formatIn.forall(c => c.toInt > 0 && c.toInt < 128), "format strings must comprise non-null ASCII values")
+ def format: String = {
+ def escaped(x: Char) = {
+ require(x.toInt >= 0)
+ if (x == '"' || x == '\\') {
+ s"\\${x}"
+ } else if (x == '\n') {
+ "\\n"
+ } else {
+ require(x.toInt >= 32) // TODO \xNN once FIRRTL issue #59 is resolved
+ x
+ }
+ }
+ formatIn.map(escaped _).mkString
+ }
+}
+
+case class Circuit(name: String, components: Seq[Component])