diff options
| -rw-r--r-- | src/main/scala/Chisel/Aggregate.scala | 34 | ||||
| -rw-r--r-- | src/test/scala/chiselTests/OptionBundle.scala | 62 |
2 files changed, 90 insertions, 6 deletions
diff --git a/src/main/scala/Chisel/Aggregate.scala b/src/main/scala/Chisel/Aggregate.scala index 1506833c..35b8b4e5 100644 --- a/src/main/scala/Chisel/Aggregate.scala +++ b/src/main/scala/Chisel/Aggregate.scala @@ -254,28 +254,50 @@ class Bundle extends Aggregate(NO_DIR) { lazy val elements: ListMap[String, Data] = ListMap(namedElts:_*) /** Returns a best guess at whether a field in this Bundle is a user-defined - * Bundle element. + * Bundle element without looking at type signatures. */ private def isBundleField(m: java.lang.reflect.Method) = m.getParameterTypes.isEmpty && !java.lang.reflect.Modifier.isStatic(m.getModifiers) && - classOf[Data].isAssignableFrom(m.getReturnType) && !(Bundle.keywords contains m.getName) && !(m.getName contains '$') + /** Returns a field's contained user-defined Bundle element if it appears to + * be one, otherwise returns None. + */ + private def getBundleField(m: java.lang.reflect.Method): Option[Data] = { + if (isBundleField(m) && + (classOf[Data].isAssignableFrom(m.getReturnType) || + classOf[Option[_]].isAssignableFrom(m.getReturnType))) { + m.invoke(this) match { + case d: Data => + Some(d) + case o: Option[_] => + o.getOrElse(None) match { + case d: Data => + Some(d) + case _ => None + } + case _ => None + } + } else { + None + } + } + /** Returns a list of elements in this Bundle. */ private[Chisel] lazy val namedElts = { val nameMap = LinkedHashMap[String, Data]() val seen = HashSet[Data]() - for (m <- getClass.getMethods.sortWith(_.getName < _.getName); if isBundleField(m)) { - m.invoke(this) match { - case d: Data => + for (m <- getClass.getMethods.sortWith(_.getName < _.getName)) { + getBundleField(m) match { + case Some(d) => if (nameMap contains m.getName) { require(nameMap(m.getName) eq d) } else if (!seen(d)) { nameMap(m.getName) = d; seen += d } - case _ => + case None => } } ArrayBuffer(nameMap.toSeq:_*) sortWith {case ((an, a), (bn, b)) => (a._id > b._id) || ((a eq b) && (an > bn))} diff --git a/src/test/scala/chiselTests/OptionBundle.scala b/src/test/scala/chiselTests/OptionBundle.scala new file mode 100644 index 00000000..1b7be1ec --- /dev/null +++ b/src/test/scala/chiselTests/OptionBundle.scala @@ -0,0 +1,62 @@ +// See LICENSE for license details. + +package chiselTests + +import org.scalatest._ +import Chisel._ +import Chisel.testers.BasicTester + +class OptionBundle(hasIn: Boolean) extends Bundle { + val in = if (hasIn) { + Some(Bool(INPUT)) + } else { + None + } + val out = Bool(OUTPUT) +} + +class OptionBundleModule(hasIn: Boolean) extends Module { + val io = new OptionBundle(hasIn) + if (hasIn) { + io.out := io.in.get + } else { + io.out := Bool(false) + } +} + +class SomeOptionBundleTester(expected: Boolean) extends BasicTester { + val mod = Module(new OptionBundleModule(true)) + mod.io.in.get := Bool(expected) + io.error := mod.io.out != Bool(expected) + io.done := Bool(true) +} + +class NoneOptionBundleTester() extends BasicTester { + val mod = Module(new OptionBundleModule(true)) + io.error := mod.io.out != Bool(false) + io.done := Bool(true) +} + +class InvalidOptionBundleTester() extends BasicTester { + val mod = Module(new OptionBundleModule(false)) + mod.io.in.get := Bool(true) + io.error := UInt(1) + io.done := Bool(true) +} + +class OptionBundleSpec extends ChiselFlatSpec { + "A Bundle with an Option field" should "work properly if the Option field is not None" in { + assert(execute { new SomeOptionBundleTester(true) }) + assert(execute { new SomeOptionBundleTester(false) }) + } + + "A Bundle with an Option field" should "compile if the Option field is None" in { + assert(execute { new NoneOptionBundleTester() }) + } + + "A Bundle with an Option field" should "assert out accessing a None Option field" in { + a [Exception] should be thrownBy { + elaborate { new InvalidOptionBundleTester() } + } + } +} |
