Skip to content
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

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 43 additions & 1 deletion chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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) }
Copy link
Contributor

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

}

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
Copy link
Contributor

Choose a reason for hiding this comment

The 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.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem is that bind() is private[chisel3] which makes it impossible to do that in user-land right now.

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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand All @@ -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")
Copy link
Contributor

Choose a reason for hiding this comment

The 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
Expand Down
2 changes: 1 addition & 1 deletion src/main/scala/chisel3/testers/BasicTester.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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))})
Copy link
Contributor Author

Choose a reason for hiding this comment

The 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')

Expand Down
122 changes: 122 additions & 0 deletions src/test/scala/chiselTests/BundleLiterals.scala
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 ))
}
}