-
Notifications
You must be signed in to change notification settings - Fork 622
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Aggregates can now be marked as literals. #694
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,9 +15,42 @@ import chisel3.internal.sourceinfo._ | |
* of) other Data objects. | ||
*/ | ||
sealed abstract class Aggregate extends Data { | ||
protected def childLitArgs = getElements.map(_.litArg).foldLeft[Option[Seq[LitArg]]](Some(Seq[LitArg]())) { | ||
case (Some(elems), Some(lit)) => Some(elems :+ lit) | ||
case _ => None | ||
} | ||
override def litArg = { | ||
// TODO[grebe] make a lit for Aggregates | ||
childLitArgs.map { x => ULit(0, 1.W) } | ||
} | ||
|
||
private var checkedBinding = false | ||
private def checkBinding = { | ||
// check if this aggregate is a literal, and bind it if it hasn't been bound yet | ||
if (!checkedBinding && getElements.length > 0 && childLitArgs.isDefined) { | ||
this.bind(LitBinding()) //, SpecifiedDirection.Unspecified) | ||
} | ||
checkedBinding = true | ||
} | ||
|
||
protected[core] override def hasBinding = { | ||
checkBinding | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This looks kind of hacky, kind of like a game of whackamole to insert checks into all the right places, it would make more sense for higher-level code to apply the binding as it is being set to a literal. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The problem is that bind() is |
||
super.hasBinding | ||
} | ||
|
||
protected[core] override def binding = { | ||
checkBinding | ||
super.binding | ||
} | ||
|
||
private[chisel3] override def bind(target: Binding, parentDirection: SpecifiedDirection) { | ||
binding = target | ||
|
||
if (isLit) { | ||
getElements.foreach { elem => require(elem.isLit()) } | ||
return () | ||
} | ||
|
||
val resolvedDirection = SpecifiedDirection.fromParent(parentDirection, specifiedDirection) | ||
for (child <- getElements) { | ||
child.bind(ChildBinding(this), resolvedDirection) | ||
|
@@ -448,6 +481,11 @@ abstract class Record(private[chisel3] implicit val compileOptions: CompileOptio | |
|
||
private[chisel3] final def allElements: Seq[Element] = elements.toIndexedSeq.flatMap(_._2.allElements) | ||
|
||
protected override def childLitArgs = elements.map({ case (_, data) => data.litArg }).foldLeft[Option[Seq[LitArg]]](Some(Seq[LitArg]())) { | ||
case (Some(elems), Some(lit)) => Some(elems :+ lit) | ||
case _ => None | ||
} | ||
|
||
override def getElements: Seq[Data] = elements.toIndexedSeq.map(_._2) | ||
|
||
// Helper because Bundle elements are reversed before printing | ||
|
@@ -553,7 +591,7 @@ class Bundle(implicit compileOptions: CompileOptions) extends Record { | |
// - A one-parameter constructor for a nested Bundle, with the enclosing | ||
// parent Module as the argument | ||
val constructor = this.getClass.getConstructors.head | ||
try { | ||
val newBundle = try { | ||
val args = Seq.fill(constructor.getParameterTypes.size)(null) | ||
constructor.newInstance(args:_*).asInstanceOf[this.type] | ||
} catch { | ||
|
@@ -570,6 +608,10 @@ class Bundle(implicit compileOptions: CompileOptions) extends Record { | |
Builder.exception(s"Parameterized Bundle ${this.getClass} needs cloneType method") | ||
this | ||
} | ||
|
||
require(!newBundle.isLit, s"Bundle ${this.getClass} has a bad cloneType: it should never return a literal") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This require won't fire if the user overrides cloneType (essentially always given your current examples), right? |
||
|
||
newBundle.asInstanceOf[this.type] | ||
} | ||
|
||
/** Default "pretty-print" implementation | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,7 +13,7 @@ import internal.sourceinfo.SourceInfo | |
|
||
class BasicTester extends Module() { | ||
// The testbench has no IOs, rather it should communicate using printf, assert, and stop. | ||
val io = IO(new Bundle()) | ||
val io = IO(new Bundle {val dummy = Input(UInt(0.W))}) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oops, forgot to clean this up, this is no longer necessary. |
||
|
||
def popCount(n: Long): Int = n.toBinaryString.count(_=='1') | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
// See LICENSE for license details. | ||
|
||
package chiselTests | ||
|
||
import chisel3._ | ||
import org.scalatest._ | ||
|
||
// Simple bundle which might be a literal. | ||
class MyPair(xLit: Option[Int] = None, yLit: Option[Int] = None) extends Bundle { | ||
val x: UInt = xLit match { | ||
case Some(l) => l.U(4.W) | ||
case _ => UInt(4.W) | ||
} | ||
val y: UInt = yLit match { | ||
case Some(l) => l.U(4.W) | ||
case _ => UInt(4.W) | ||
} | ||
|
||
def +(that: MyPair): MyPair = { | ||
val output = Wire(MyPair()) | ||
output.x := x + that.x | ||
output.y := y + that.y | ||
output | ||
} | ||
|
||
override def cloneType = (new MyPair(xLit, yLit)).asInstanceOf[this.type] | ||
} | ||
|
||
object MyPair { | ||
// Create an unbound pair | ||
def apply(): MyPair = new MyPair() | ||
|
||
// Create a literal pair | ||
def apply(x: Int, y: Int): MyPair = { | ||
new MyPair(Some(x), Some(y)) | ||
} | ||
} | ||
|
||
// Example usecase of a user might use the MyPair type | ||
class PairModule extends Module { | ||
val io = IO(new Bundle{ | ||
val in = Input(MyPair()) | ||
val plus_one = Output(MyPair()) | ||
val vanilla = Output(MyPair()) | ||
}) | ||
val not_a_lit = new MyPair(Some(1)) | ||
assert(!not_a_lit.isLit) | ||
val only_a_type = new MyPair() | ||
assert(!only_a_type.isLit) | ||
|
||
val one = MyPair(1, 1) | ||
assert(one.isLit) | ||
io.vanilla := io.in | ||
io.plus_one := io.in + one | ||
} | ||
|
||
case class BundleWithUInt(x: UInt = UInt()) extends Bundle { | ||
override def cloneType = BundleWithUInt(x.cloneType).asInstanceOf[this.type] | ||
} | ||
|
||
case class BundleWithBundleWithUInt(x: BundleWithUInt = BundleWithUInt()) extends Bundle { | ||
override def cloneType = BundleWithBundleWithUInt(x.cloneType).asInstanceOf[this.type] | ||
} | ||
|
||
case class BundleWithUIntAndBadCloneType(x: UInt = UInt()) extends Bundle | ||
|
||
|
||
class BundleWithUIntModule extends Module { | ||
val io = IO(new Bundle { | ||
val out = Output(BundleWithUInt(UInt(4.W))) | ||
}) | ||
io.out := BundleWithUInt(10.U) | ||
} | ||
|
||
class BundleWithCloneTypeModule extends Module { | ||
val io = IO(new Bundle { | ||
val out = Output(BundleWithUInt(9.U)) // will call cloneType | ||
}) | ||
io.out := BundleWithUInt(5.U) | ||
} | ||
|
||
class BundleWithBadCloneTypeModule extends Module { | ||
val io = IO(new Bundle { | ||
val out = Output(BundleWithUIntAndBadCloneType(3.U)) | ||
}) | ||
io.out := BundleWithUIntAndBadCloneType(0.U) | ||
} | ||
|
||
class BundleWithBundleWithUIntModule extends Module { | ||
val io = IO(new Bundle { | ||
val out = Output(BundleWithBundleWithUInt(BundleWithUInt(UInt(4.W)))) | ||
}) | ||
io.out := BundleWithBundleWithUInt(BundleWithUInt(7.U)) | ||
} | ||
|
||
class BundleLiteralsSpec extends FlatSpec with Matchers { | ||
behavior of "Bundle literals" | ||
|
||
it should "check that all elements are bound and work as expected" in { | ||
println(chisel3.Driver.emitVerilog( new PairModule )) | ||
} | ||
|
||
it should "build the module without crashing" in { | ||
println(chisel3.Driver.emitVerilog( new BundleWithUIntModule )) | ||
} | ||
|
||
it should "work with a correct cloneType implementation" in { | ||
println(chisel3.Driver.emitVerilog( new BundleWithCloneTypeModule )) | ||
} | ||
|
||
it should "throw an exception if cloneType is bad" in { | ||
assertThrows[IllegalArgumentException] { | ||
println(chisel3.Driver.emitVerilog( new BundleWithBadCloneTypeModule )) | ||
} | ||
} | ||
|
||
behavior of "Bundle literals with bundle literals inside" | ||
|
||
it should "build the module without crashing" in { | ||
println(chisel3.Driver.emitVerilog( new BundleWithBundleWithUIntModule )) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Things should fail noisily if this is an unexpected case instead of propagating bad values