Skip to content

Commit

Permalink
Merge branch 'master' into buildinfo
Browse files Browse the repository at this point in the history
  • Loading branch information
ucbjrl committed Oct 6, 2016
2 parents f981712 + 7aea39d commit d9e46d0
Show file tree
Hide file tree
Showing 22 changed files with 337 additions and 61 deletions.
24 changes: 15 additions & 9 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,23 @@ site.includeScaladoc()

ghpages.settings

import UnidocKeys._

lazy val customUnidocSettings = unidocSettings ++ Seq (
doc in Compile := (doc in ScalaUnidoc).value,
target in unidoc in ScalaUnidoc := crossTarget.value / "api"
)

lazy val commonSettings = Seq (
organization := "edu.berkeley.cs",
version := "3.1-SNAPSHOT",
git.remoteRepo := "[email protected]:ucb-bar/chisel3.git",
autoAPIMappings := true,
scalaVersion := "2.11.7"
)

lazy val chiselSettings = Seq (
name := "Chisel3",
name := "chisel3",

publishMavenStyle := true,
publishArtifact in Test := false,
Expand Down Expand Up @@ -50,8 +58,8 @@ lazy val chiselSettings = Seq (
},

resolvers ++= Seq(
"Sonatype Snapshots" at "http://oss.sonatype.org/content/repositories/snapshots",
"Sonatype Releases" at "http://oss.sonatype.org/content/repositories/releases"
Resolver.sonatypeRepo("snapshots"),
Resolver.sonatypeRepo("releases")
),

/* Bumping "com.novocode" % "junit-interface" % "0.11", causes DelayTest testSeqReadBundle to fail
Expand Down Expand Up @@ -113,18 +121,16 @@ lazy val chisel = (project in file(".")).
mappings in (Compile, packageSrc) += { ((sourceManaged in Compile).value / "sbt-buildinfo" / "BuildInfo.scala") -> "BuildInfo.scala" }
).
settings(commonSettings: _*).
settings(customUnidocSettings: _*).
settings(chiselSettings: _*).
dependsOn(coreMacros).
dependsOn(chiselFrontend).
settings(
aggregate in doc := false,
// Include macro classes, resources, and sources main jar.
mappings in (Compile, packageBin) <++= mappings in (coreMacros, Compile, packageBin),
mappings in (Compile, packageSrc) <++= mappings in (coreMacros, Compile, packageSrc),
mappings in (Compile, packageBin) <++= mappings in (chiselFrontend, Compile, packageBin),
mappings in (Compile, packageSrc) <++= mappings in (chiselFrontend, Compile, packageSrc)
)

// This is ugly. There must be a better way.
publish <<= (publish) dependsOn (publish in coreMacros, publish in chiselFrontend)

publishLocal <<= (publishLocal) dependsOn (publishLocal in coreMacros, publishLocal in chiselFrontend)
).
aggregate(coreMacros, chiselFrontend)
16 changes: 14 additions & 2 deletions chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,16 @@ import scala.language.experimental.macros
import chisel3.internal._
import chisel3.internal.Builder.pushCommand
import chisel3.internal.firrtl._
import chisel3.internal.sourceinfo.{SourceInfo, DeprecatedSourceInfo, VecTransform, SourceInfoTransform}
import chisel3.internal.sourceinfo.{SourceInfo, DeprecatedSourceInfo, VecTransform, SourceInfoTransform, UnlocatableSourceInfo}

/** An abstract class for data types that solely consist of (are an aggregate
* of) other Data objects.
*/
sealed abstract class Aggregate extends Data {
private[core] def cloneTypeWidth(width: Width): this.type = cloneType
private[core] def width: Width = flatten.map(_.width).reduce(_ + _)
private[core] def legacyConnect(that: Data)(implicit sourceInfo: SourceInfo): Unit =
pushCommand(BulkConnect(sourceInfo, this.lref, that.lref))
}

object Vec {
Expand Down Expand Up @@ -113,6 +115,15 @@ object Vec {

def do_fill[T <: Data](n: Int)(gen: => T)(implicit sourceInfo: SourceInfo): Vec[T] =
apply(Seq.fill(n)(gen))

/** Truncate an index to implement modulo-power-of-2 addressing. */
private[core] def truncateIndex(idx: UInt, n: Int)(implicit sourceInfo: SourceInfo): UInt = {
val w = BigInt(n-1).bitLength
if (n <= 1) UInt(0)
else if (idx.width.known && idx.width.get <= w) idx
else if (idx.width.known) idx(w-1,0)
else Wire(UInt(width = w), init = idx)
}
}

/** A vector (array) of [[Data]] elements. Provides hardware versions of various
Expand Down Expand Up @@ -178,7 +189,8 @@ sealed class Vec[T <: Data] private (gen: T, val length: Int)
def apply(idx: UInt): T = {
Binding.checkSynthesizable(idx ,s"'idx' ($idx)")
val port = sample_element.chiselCloneType
port.setRef(this, idx) //TODO(twigg): This is a bit too magical
val i = Vec.truncateIndex(idx, length)(UnlocatableSourceInfo)
port.setRef(this, i)

// Bind each element of port to being whatever the base type is
// Using the head element as the sample_element
Expand Down
13 changes: 7 additions & 6 deletions chiselFrontend/src/main/scala/chisel3/core/Assert.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,21 +24,22 @@ object assert { // scalastyle:ignore object.name
*
* @param cond condition, assertion fires (simulation fails) when false
* @param message optional message to print when the assertion fires
* @param data optional bits to print in the message formatting
*
* @note currently cannot be used in core Chisel / libraries because macro
* defs need to be compiled first and the SBT project is not set up to do
* that
*/
// Macros currently can't take default arguments, so we need two functions to emulate defaults.
def apply(cond: Bool, message: String)(implicit sourceInfo: SourceInfo): Unit = macro apply_impl_msg
def apply(cond: Bool, message: String, data: Bits*)(implicit sourceInfo: SourceInfo): Unit = macro apply_impl_msg_data
def apply(cond: Bool)(implicit sourceInfo: SourceInfo): Unit = macro apply_impl

def apply_impl_msg(c: Context)(cond: c.Tree, message: c.Tree)(sourceInfo: c.Tree): c.Tree = {
def apply_impl_msg_data(c: Context)(cond: c.Tree, message: c.Tree, data: c.Tree*)(sourceInfo: c.Tree): c.Tree = {
import c.universe._
val p = c.enclosingPosition
val condStr = s"${p.source.file.name}:${p.line} ${p.lineContent.trim}"
val apply_impl_do = symbolOf[this.type].asClass.module.info.member(TermName("apply_impl_do"))
q"$apply_impl_do($cond, $condStr, _root_.scala.Some($message))($sourceInfo)"
q"$apply_impl_do($cond, $condStr, _root_.scala.Some($message), ..$data)($sourceInfo)"
}

def apply_impl(c: Context)(cond: c.Tree)(sourceInfo: c.Tree): c.Tree = {
Expand All @@ -49,11 +50,11 @@ object assert { // scalastyle:ignore object.name
q"$apply_impl_do($cond, $condStr, _root_.scala.None)($sourceInfo)"
}

def apply_impl_do(cond: Bool, line: String, message: Option[String])(implicit sourceInfo: SourceInfo) {
def apply_impl_do(cond: Bool, line: String, message: Option[String], data: Bits*)(implicit sourceInfo: SourceInfo) {
when (!(cond || Builder.forcedModule.reset)) {
message match {
case Some(str) => printf.printfWithoutReset(s"Assertion failed: $str\n at $line\n")
case None => printf.printfWithoutReset(s"Assertion failed\n at $line\n")
case Some(str) => printf.printfWithoutReset(s"Assertion failed: $str\n at $line\n", data:_*)
case None => printf.printfWithoutReset(s"Assertion failed\n at $line\n", data:_*)
}
pushCommand(Stop(sourceInfo, Node(Builder.forcedModule.clock), 1))
}
Expand Down
36 changes: 33 additions & 3 deletions chiselFrontend/src/main/scala/chisel3/core/Binding.scala
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ object Binding {
case class BindingException(message: String) extends Exception(message)
def AlreadyBoundException(binding: String) = BindingException(s": Already bound to $binding")
def NotSynthesizableException = BindingException(s": Not bound to synthesizable node, currently only Type description")
def MissingIOWrapperException = BindingException(": Missing IO() wrapper")

// This recursively walks down the Data tree to look at all the leaf 'Element's
// Will build up an error string in case something goes wrong
Expand Down Expand Up @@ -138,17 +139,46 @@ object Binding {
}
}
}

/** Diagnose a binding error caused by a missing IO() wrapper.
* @param element the element triggering the binding error.
* @return true if the element is a member of the module's io but ioDefined is false.
*/
def isMissingIOWrapper(element: Element): Boolean = {
element._parent match {
case None => false
case Some(x: Module) => {
// If the IO() wrapper has been executed, it isn't missing.
if (x.ioDefined) {
false
} else {
// TODO: We should issue the message only once, and if we get here,
// we know the wrapper is missing, whether or not the element is a member of io.
// But if it's not an io element, we want to issue the complementary "unbound" error.
// Revisit this when we collect error messages instead of throwing exceptions.
x.io.flatten.contains(element)
}
}
}
}

try walkToBinding(
target,
element => element.binding match {
case SynthesizableBinding() => {} // OK
case binding =>
// The following kludge is an attempt to provide backward compatibility
// It should be done at at higher level.
if ((forcedModule.compileOptions.requireIOWrap || !elementOfIO(element)))
throw NotSynthesizableException
else
if ((forcedModule.compileOptions.requireIOWrap || !elementOfIO(element))) {
// Generate a better error message if this is a result of a missing IO() wrapper.
if (isMissingIOWrapper(element)) {
throw MissingIOWrapperException
} else {
throw NotSynthesizableException
}
} else {
Binding.bind(element, PortBinder(element._parent.get), "Error: IO")
}
}
)
catch {
Expand Down
5 changes: 4 additions & 1 deletion chiselFrontend/src/main/scala/chisel3/core/Bits.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ package chisel3.core
import scala.language.experimental.macros

import chisel3.internal._
import chisel3.internal.Builder.pushOp
import chisel3.internal.Builder.{pushCommand, pushOp}
import chisel3.internal.firrtl._
import chisel3.internal.sourceinfo.{SourceInfo, DeprecatedSourceInfo, SourceInfoTransform, SourceInfoWhiteboxTransform,
UIntTransform, MuxTransform}
Expand Down Expand Up @@ -40,6 +40,9 @@ abstract class Element(private[core] val width: Width) extends Data {
private[chisel3] final def allElements: Seq[Element] = Seq(this)
def widthKnown: Boolean = width.known
def name: String = getRef.name

private[core] def legacyConnect(that: Data)(implicit sourceInfo: SourceInfo): Unit =
pushCommand(Connect(sourceInfo, this.lref, that.ref))
}

/** A data type for values represented by a single bitvector. Provides basic
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ trait CompileOptions {
val dontTryConnectionsSwapped: Boolean
// If connection directionality is not explicit, do not use heuristics to attempt to determine it.
val dontAssumeDirectionality: Boolean
// Issue a deprecation warning if Data.{flip, asInput,asOutput} is used
// instead of Flipped, Input, or Output.
val deprecateOldDirectionMethods: Boolean
// Check that referenced Data have actually been declared.
val checkSynthesizable: Boolean
}

object CompileOptions {
Expand All @@ -34,6 +39,8 @@ object ExplicitCompileOptions {
val requireIOWrap = false
val dontTryConnectionsSwapped = false
val dontAssumeDirectionality = false
val deprecateOldDirectionMethods = false
val checkSynthesizable = false
}

// Collection of "strict" connection compile options, preferred for new code.
Expand All @@ -44,5 +51,7 @@ object ExplicitCompileOptions {
val requireIOWrap = true
val dontTryConnectionsSwapped = true
val dontAssumeDirectionality = true
val deprecateOldDirectionMethods = true
val checkSynthesizable = true
}
}
79 changes: 55 additions & 24 deletions chiselFrontend/src/main/scala/chisel3/core/Data.scala
Original file line number Diff line number Diff line change
Expand Up @@ -104,12 +104,21 @@ object Data {
}

implicit class AddDirectionToData[T<:Data](val target: T) extends AnyVal {
@deprecated("Input(Data) should be used over Data.asInput", "gchisel")
def asInput: T = Input(target)
@deprecated("Output(Data) should be used over Data.asOutput", "gchisel")
def asOutput: T = Output(target)
@deprecated("Flipped(Data) should be used over Data.flip", "gchisel")
def flip(): T = Flipped(target)
def asInput(implicit opts: CompileOptions): T = {
if (opts.deprecateOldDirectionMethods)
Builder.deprecated("Input(Data) should be used over Data.asInput")
Input(target)
}
def asOutput(implicit opts: CompileOptions): T = {
if (opts.deprecateOldDirectionMethods)
Builder.deprecated("Output(Data) should be used over Data.asOutput")
Output(target)
}
def flip()(implicit opts: CompileOptions): T = {
if (opts.deprecateOldDirectionMethods)
Builder.deprecated("Flipped(Data) should be used over Data.flip")
Flipped(target)
}
}
}

Expand All @@ -126,43 +135,65 @@ abstract class Data extends HasId {
private[core] def badConnect(that: Data)(implicit sourceInfo: SourceInfo): Unit =
throwException(s"cannot connect ${this} and ${that}")
private[chisel3] def connect(that: Data)(implicit sourceInfo: SourceInfo, connectCompileOptions: CompileOptions): Unit = {
Binding.checkSynthesizable(this, s"'this' ($this)")
Binding.checkSynthesizable(that, s"'that' ($that)")
try {
MonoConnect.connect(sourceInfo, connectCompileOptions, this, that, Builder.forcedModule)
} catch {
case MonoConnect.MonoConnectException(message) =>
throwException(
s"Connection between sink ($this) and source ($that) failed @$message"
)
if (connectCompileOptions.checkSynthesizable) {
Binding.checkSynthesizable(this, s"'this' ($this)")
Binding.checkSynthesizable(that, s"'that' ($that)")
try {
MonoConnect.connect(sourceInfo, connectCompileOptions, this, that, Builder.forcedModule)
} catch {
case MonoConnect.MonoConnectException(message) =>
throwException(
s"Connection between sink ($this) and source ($that) failed @$message"
)
}
} else {
this legacyConnect that
}
}
private[chisel3] def bulkConnect(that: Data)(implicit sourceInfo: SourceInfo, connectCompileOptions: CompileOptions): Unit = {
Binding.checkSynthesizable(this, s"'this' ($this)")
Binding.checkSynthesizable(that, s"'that' ($that)")
try {
BiConnect.connect(sourceInfo, connectCompileOptions, this, that, Builder.forcedModule)
} catch {
case BiConnect.BiConnectException(message) =>
throwException(
s"Connection between left ($this) and source ($that) failed @$message"
)
if (connectCompileOptions.checkSynthesizable) {
Binding.checkSynthesizable(this, s"'this' ($this)")
Binding.checkSynthesizable(that, s"'that' ($that)")
try {
BiConnect.connect(sourceInfo, connectCompileOptions, this, that, Builder.forcedModule)
} catch {
case BiConnect.BiConnectException(message) =>
throwException(
s"Connection between left ($this) and source ($that) failed @$message"
)
}
} else {
this legacyConnect that
}
}
private[chisel3] def lref: Node = Node(this)
private[chisel3] def ref: Arg = if (isLit) litArg.get else lref
private[core] def cloneTypeWidth(width: Width): this.type
private[chisel3] def toType: String
private[core] def width: Width
private[core] def legacyConnect(that: Data)(implicit sourceInfo: SourceInfo): Unit

/** cloneType must be defined for any Chisel object extending Data.
* It is responsible for constructing a basic copy of the object being cloned.
* If cloneType needs to recursively clone elements of an object, it should call
* the cloneType methods on those elements.
* @return a copy of the object.
*/
def cloneType: this.type

/** chiselCloneType is called at the top-level of a clone chain.
* It calls the client's cloneType() method to construct a basic copy of the object being cloned,
* then performs any fixups required to reconstruct the appropriate core state of the cloned object.
* @return a copy of the object with appropriate core state.
*/
def chiselCloneType: this.type = {
// Call the user-supplied cloneType method
val clone = this.cloneType
Data.setFirrtlDirection(clone, Data.getFirrtlDirection(this))
//TODO(twigg): Do recursively for better error messages
for((clone_elem, source_elem) <- clone.allElements zip this.allElements) {
clone_elem.binding = UnboundBinding(source_elem.binding.direction)
Data.setFirrtlDirection(clone_elem, Data.getFirrtlDirection(source_elem))
}
clone
}
Expand Down
8 changes: 6 additions & 2 deletions chiselFrontend/src/main/scala/chisel3/core/Mem.scala
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,10 @@ sealed abstract class MemBase[T <: Data](t: T, val length: Int) extends HasId wi
/** Creates a read accessor into the memory with static addressing. See the
* class documentation of the memory for more detailed information.
*/
def apply(idx: Int): T = apply(UInt(idx))
def apply(idx: Int): T = {
require(idx >= 0 && idx < length)
apply(UInt(idx))
}

/** Creates a read/write accessor into the memory with dynamic addressing.
* See the class documentation of the memory for more detailed information.
Expand Down Expand Up @@ -85,10 +88,11 @@ sealed abstract class MemBase[T <: Data](t: T, val length: Int) extends HasId wi

private def makePort(sourceInfo: SourceInfo, idx: UInt, dir: MemPortDirection): T = {
Binding.checkSynthesizable(idx, s"'idx' ($idx)")
val i = Vec.truncateIndex(idx, length)(sourceInfo)

val port = pushCommand(
DefMemPort(sourceInfo,
t.chiselCloneType, Node(this), dir, idx.ref, Node(idx._parent.get.clock))
t.chiselCloneType, Node(this), dir, i.ref, Node(i._parent.get.clock))
).id
// Bind each element of port to being a MemoryPort
Binding.bind(port, MemoryPortBinder(Builder.forcedModule), "Error: Fresh t")
Expand Down
2 changes: 2 additions & 0 deletions chiselFrontend/src/main/scala/chisel3/internal/Builder.scala
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,8 @@ private[chisel3] object Builder {

def errors: ErrorLog = dynamicContext.errors
def error(m: => String): Unit = errors.error(m)
def warning(m: => String): Unit = errors.warning(m)
def deprecated(m: => String): Unit = errors.deprecated(m)

def build[T <: Module](f: => T): Circuit = {
dynamicContextVar.withValue(Some(new DynamicContext())) {
Expand Down
Loading

0 comments on commit d9e46d0

Please sign in to comment.