summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJack2022-04-26 02:53:08 +0000
committerJack2022-04-26 02:53:08 +0000
commit3a6cc75d72cbf890bbd45a002c31d16abfc6896d (patch)
tree298a39cbdbcd7e89953d75dbd840884f29ff7699
parentbe1ac06bf20c6c3d84c8ce5b0a50e2980e546e7e (diff)
parentd5a964f6e7beea1f38f9623224fc65e2397e1fe7 (diff)
Merge branch '3.5.x' into 3.5-release
-rw-r--r--core/src/main/scala/chisel3/Aggregate.scala28
-rw-r--r--core/src/main/scala/chisel3/Bits.scala4
-rw-r--r--core/src/main/scala/chisel3/BlackBox.scala2
-rw-r--r--core/src/main/scala/chisel3/Clock.scala2
-rw-r--r--core/src/main/scala/chisel3/Data.scala2
-rw-r--r--core/src/main/scala/chisel3/Element.scala11
-rw-r--r--core/src/main/scala/chisel3/Mem.scala10
-rw-r--r--core/src/main/scala/chisel3/Printable.scala2
-rw-r--r--core/src/main/scala/chisel3/VerificationStatement.scala4
-rw-r--r--core/src/main/scala/chisel3/experimental/dataview/package.scala55
-rw-r--r--core/src/main/scala/chisel3/experimental/hierarchy/Lookupable.scala16
-rw-r--r--core/src/main/scala/chisel3/experimental/package.scala28
-rw-r--r--core/src/main/scala/chisel3/internal/BiConnect.scala8
-rw-r--r--core/src/main/scala/chisel3/internal/Binding.scala11
-rw-r--r--core/src/main/scala/chisel3/internal/Builder.scala49
-rw-r--r--docs/src/cookbooks/cookbook.md45
-rw-r--r--macros/src/main/scala/chisel3/internal/naming/NamingAnnotations.scala2
-rw-r--r--plugin/src/main/scala/chisel3/internal/plugin/BundleComponent.scala2
-rw-r--r--src/main/scala/chisel3/util/BlackBoxUtils.scala8
-rw-r--r--src/main/scala/chisel3/util/ExtModuleUtils.scala8
-rw-r--r--src/test/scala/chiselTests/BlackBox.scala29
-rw-r--r--src/test/scala/chiselTests/BulkConnectSpec.scala20
-rw-r--r--src/test/scala/chiselTests/InvalidateAPISpec.scala8
-rw-r--r--src/test/scala/chiselTests/Mem.scala52
-rw-r--r--src/test/scala/chiselTests/ReduceTreeSpec.scala106
-rw-r--r--src/test/scala/chiselTests/experimental/DataView.scala30
-rw-r--r--src/test/scala/chiselTests/experimental/DataViewTargetSpec.scala4
-rw-r--r--src/test/scala/chiselTests/experimental/FlatIOSpec.scala51
28 files changed, 496 insertions, 101 deletions
diff --git a/core/src/main/scala/chisel3/Aggregate.scala b/core/src/main/scala/chisel3/Aggregate.scala
index 06ae36f3..cc5b83d9 100644
--- a/core/src/main/scala/chisel3/Aggregate.scala
+++ b/core/src/main/scala/chisel3/Aggregate.scala
@@ -14,6 +14,7 @@ import chisel3.internal.Builder.pushCommand
import chisel3.internal.firrtl._
import chisel3.internal.sourceinfo._
+import java.lang.Math.{floor, log10, pow}
import scala.collection.mutable
class AliasedAggregateFieldException(message: String) extends ChiselException(message)
@@ -381,11 +382,30 @@ sealed class Vec[T <: Data] private[chisel3] (gen: => T, val length: Int) extend
compileOptions: CompileOptions
): T = {
require(!isEmpty, "Cannot apply reduction on a vec of size 0")
- var curLayer: Seq[T] = this
- while (curLayer.length > 1) {
- curLayer = curLayer.grouped(2).map(x => if (x.length == 1) layerOp(x(0)) else redOp(x(0), x(1))).toSeq
+
+ def recReduce[T](s: Seq[T], op: (T, T) => T, lop: (T) => T): T = {
+
+ val n = s.length
+ n match {
+ case 1 => lop(s(0))
+ case 2 => op(s(0), s(1))
+ case _ =>
+ val m = pow(2, floor(log10(n - 1) / log10(2))).toInt // number of nodes in next level, will be a power of 2
+ val p = 2 * m - n // number of nodes promoted
+
+ val l = s.take(p).map(lop)
+ val r = s
+ .drop(p)
+ .grouped(2)
+ .map {
+ case Seq(a, b) => op(a, b)
+ }
+ .toVector
+ recReduce(l ++ r, op, lop)
+ }
}
- curLayer(0)
+
+ recReduce(this, redOp, layerOp)
}
/** Creates a Vec literal of this type with specified values. this must be a chisel type.
diff --git a/core/src/main/scala/chisel3/Bits.scala b/core/src/main/scala/chisel3/Bits.scala
index 4133592f..a135a8e5 100644
--- a/core/src/main/scala/chisel3/Bits.scala
+++ b/core/src/main/scala/chisel3/Bits.scala
@@ -1324,7 +1324,7 @@ sealed class Bool() extends UInt(1.W) with Reset {
/** Logical or operator
*
* @param that a hardware $coll
- * @return the lgocial or of this $coll and `that`
+ * @return the logical or of this $coll and `that`
* @note this is equivalent to [[Bool!.|(that:chisel3\.Bool)* Bool.|)]]
* @group Logical
*/
@@ -1336,7 +1336,7 @@ sealed class Bool() extends UInt(1.W) with Reset {
/** Logical and operator
*
* @param that a hardware $coll
- * @return the lgocial and of this $coll and `that`
+ * @return the logical and of this $coll and `that`
* @note this is equivalent to [[Bool!.&(that:chisel3\.Bool)* Bool.&]]
* @group Logical
*/
diff --git a/core/src/main/scala/chisel3/BlackBox.scala b/core/src/main/scala/chisel3/BlackBox.scala
index 2f04fb2e..f3fc2711 100644
--- a/core/src/main/scala/chisel3/BlackBox.scala
+++ b/core/src/main/scala/chisel3/BlackBox.scala
@@ -157,7 +157,7 @@ abstract class BlackBox(
_compatAutoWrapPorts() // pre-IO(...) compatibility hack
// Restrict IO to just io, clock, and reset
- if (!_io.forall(portsContains)) {
+ if (!_io.exists(portsContains)) {
throwException(s"BlackBox '$this' must have a port named 'io' of type Record wrapped in IO(...)!")
}
diff --git a/core/src/main/scala/chisel3/Clock.scala b/core/src/main/scala/chisel3/Clock.scala
index 68174d7c..64e91c42 100644
--- a/core/src/main/scala/chisel3/Clock.scala
+++ b/core/src/main/scala/chisel3/Clock.scala
@@ -23,7 +23,7 @@ sealed class Clock(private[chisel3] val width: Width = Width(1)) extends Element
override def connect(that: Data)(implicit sourceInfo: SourceInfo, connectCompileOptions: CompileOptions): Unit =
that match {
- case _: Clock => super.connect(that)(sourceInfo, connectCompileOptions)
+ case _: Clock | DontCare => super.connect(that)(sourceInfo, connectCompileOptions)
case _ => super.badConnect(that)(sourceInfo)
}
diff --git a/core/src/main/scala/chisel3/Data.scala b/core/src/main/scala/chisel3/Data.scala
index ef43a3b0..f468335e 100644
--- a/core/src/main/scala/chisel3/Data.scala
+++ b/core/src/main/scala/chisel3/Data.scala
@@ -667,7 +667,7 @@ abstract class Data extends HasId with NamedComponent with SourceInfoDoc {
topBindingOpt match {
// DataView
case Some(ViewBinding(target)) => reify(target).ref
- case Some(AggregateViewBinding(viewMap, _)) =>
+ case Some(AggregateViewBinding(viewMap)) =>
viewMap.get(this) match {
case None => materializeWire() // FIXME FIRRTL doesn't have Aggregate Init expressions
// This should not be possible because Element does the lookup in .topBindingOpt
diff --git a/core/src/main/scala/chisel3/Element.scala b/core/src/main/scala/chisel3/Element.scala
index 401f2bdf..39b7689c 100644
--- a/core/src/main/scala/chisel3/Element.scala
+++ b/core/src/main/scala/chisel3/Element.scala
@@ -36,10 +36,15 @@ abstract class Element extends Data {
case Some(litArg) => Some(ElementLitBinding(litArg))
case _ => Some(DontCareBinding())
}
- case Some(b @ AggregateViewBinding(viewMap, _)) =>
+ // TODO Do we even need this? Looking up things in the AggregateViewBinding is fine
+ case Some(b @ AggregateViewBinding(viewMap)) =>
viewMap.get(this) match {
- case Some(elt) => Some(ViewBinding(elt))
- case _ => throwException(s"Internal Error! $this missing from topBinding $b")
+ case Some(elt: Element) => Some(ViewBinding(elt))
+ // TODO We could generate a reduced AggregateViewBinding, but is there a point?
+ // Generating the new object would be somewhat slow, it's not clear if we should do this
+ // matching anyway
+ case Some(data: Aggregate) => Some(b)
+ case _ => throwException(s"Internal Error! $this missing from topBinding $b")
}
case topBindingOpt => topBindingOpt
}
diff --git a/core/src/main/scala/chisel3/Mem.scala b/core/src/main/scala/chisel3/Mem.scala
index 36984a3a..d6ab9c4b 100644
--- a/core/src/main/scala/chisel3/Mem.scala
+++ b/core/src/main/scala/chisel3/Mem.scala
@@ -56,7 +56,9 @@ sealed abstract class MemBase[T <: Data](val t: T, val length: BigInt)
with SourceInfoDoc {
_parent.foreach(_.addId(this))
- private val clockInst: Clock = Builder.forcedClock
+ // if the memory is created in a scope with an implicit clock (-> clockInst is defined), we will perform checks that
+ // ensure memory ports are created with the same clock unless explicitly specified to use a different clock
+ private val clockInst: Option[Clock] = Builder.currentClock
protected def clockWarning(sourceInfo: Option[SourceInfo]): Unit = {
// Turn into pretty String if possible, if not, Builder.deprecated will find one via stack trace
@@ -132,7 +134,7 @@ sealed abstract class MemBase[T <: Data](val t: T, val length: BigInt)
implicit sourceInfo: SourceInfo,
compileOptions: CompileOptions
): T = {
- if (warn && clock != clockInst) {
+ if (warn && clockInst.isDefined && clock != clockInst.get) {
clockWarning(Some(sourceInfo))
}
makePort(sourceInfo, idx, dir, clock)
@@ -164,7 +166,7 @@ sealed abstract class MemBase[T <: Data](val t: T, val length: BigInt)
)(
implicit compileOptions: CompileOptions
): Unit = {
- if (warn && clock != clockInst) {
+ if (warn && clockInst.isDefined && clock != clockInst.get) {
clockWarning(None)
}
implicit val sourceInfo = UnlocatableSourceInfo
@@ -223,7 +225,7 @@ sealed abstract class MemBase[T <: Data](val t: T, val length: BigInt)
compileOptions: CompileOptions
): Unit = {
implicit val sourceInfo = UnlocatableSourceInfo
- if (warn && clock != clockInst) {
+ if (warn && clockInst.isDefined && clock != clockInst.get) {
clockWarning(None)
}
val accessor = makePort(sourceInfo, idx, MemPortDirection.WRITE, clock).asInstanceOf[Vec[Data]]
diff --git a/core/src/main/scala/chisel3/Printable.scala b/core/src/main/scala/chisel3/Printable.scala
index c477716b..a616f2b0 100644
--- a/core/src/main/scala/chisel3/Printable.scala
+++ b/core/src/main/scala/chisel3/Printable.scala
@@ -18,7 +18,7 @@ import java.util.{MissingFormatArgumentException, UnknownFormatConversionExcepti
* }}}
* This is equivalent to writing:
* {{{
- * printf(p"The value of wire = %d\n", wire)
+ * printf("The value of wire = %d\n", wire)
* }}}
* All Chisel data types have a method `.toPrintable` that gives a default pretty print that can be
* accessed via `p"..."`. This works even for aggregate types, for example:
diff --git a/core/src/main/scala/chisel3/VerificationStatement.scala b/core/src/main/scala/chisel3/VerificationStatement.scala
index bfdfc26e..7229c412 100644
--- a/core/src/main/scala/chisel3/VerificationStatement.scala
+++ b/core/src/main/scala/chisel3/VerificationStatement.scala
@@ -92,8 +92,8 @@ object assert {
): Assert = {
val id = new Assert()
when(!Module.reset.asBool()) {
- Builder.pushCommand(Verification(id, Formal.Assert, sourceInfo, Module.clock.ref, cond.ref, ""))
failureMessage("Assertion", line, cond, message, data)
+ Builder.pushCommand(Verification(id, Formal.Assert, sourceInfo, Module.clock.ref, cond.ref, ""))
}
id
}
@@ -178,8 +178,8 @@ object assume {
): Assume = {
val id = new Assume()
when(!Module.reset.asBool()) {
- Builder.pushCommand(Verification(id, Formal.Assume, sourceInfo, Module.clock.ref, cond.ref, ""))
failureMessage("Assumption", line, cond, message, data)
+ Builder.pushCommand(Verification(id, Formal.Assume, sourceInfo, Module.clock.ref, cond.ref, ""))
}
id
}
diff --git a/core/src/main/scala/chisel3/experimental/dataview/package.scala b/core/src/main/scala/chisel3/experimental/dataview/package.scala
index 891ecb81..c583c516 100644
--- a/core/src/main/scala/chisel3/experimental/dataview/package.scala
+++ b/core/src/main/scala/chisel3/experimental/dataview/package.scala
@@ -97,11 +97,16 @@ package object dataview {
val targetContains: Data => Boolean = implicitly[DataProduct[T]].dataSet(target)
// Resulting bindings for each Element of the View
- val childBindings =
+ // Kept separate from Aggregates for totality checking
+ val elementBindings =
new mutable.HashMap[Data, mutable.ListBuffer[Element]] ++
viewFieldLookup.view.collect { case (elt: Element, _) => elt }
.map(_ -> new mutable.ListBuffer[Element])
+ // Record any Aggregates that correspond 1:1 for reification
+ // Using Data instead of Aggregate to avoid unnecessary checks
+ val aggregateMappings = mutable.ArrayBuffer.empty[(Data, Data)]
+
def viewFieldName(d: Data): String =
viewFieldLookup.get(d).map(_ + " ").getOrElse("") + d.toString
@@ -130,7 +135,7 @@ package object dataview {
s"View field $fieldName has width ${vwidth} that is incompatible with target value $tex's width ${twidth}"
)
}
- childBindings(vex) += tex
+ elementBindings(vex) += tex
}
mapping.foreach {
@@ -145,7 +150,7 @@ package object dataview {
}
getMatchedFields(aa, ba).foreach {
case (aelt: Element, belt: Element) => onElt(aelt, belt)
- case _ => // Ignore matching of Aggregates
+ case (t, v) => aggregateMappings += (v -> t)
}
}
@@ -155,7 +160,7 @@ package object dataview {
val targetSeen: Option[mutable.Set[Data]] = if (total) Some(mutable.Set.empty[Data]) else None
- val resultBindings = childBindings.map {
+ val elementResult = elementBindings.map {
case (data, targets) =>
val targetsx = targets match {
case collection.Seq(target: Element) => target
@@ -183,23 +188,25 @@ package object dataview {
}
view match {
- case elt: Element => view.bind(ViewBinding(resultBindings(elt)))
+ case elt: Element => view.bind(ViewBinding(elementResult(elt)))
case agg: Aggregate =>
- // We record total Data mappings to provide a better .toTarget
- val topt = target match {
- case d: Data if total => Some(d)
+ // Don't forget the potential mapping of the view to the target!
+ target match {
+ case d: Data if total =>
+ aggregateMappings += (agg -> d)
case _ =>
- // Record views that don't have the simpler .toTarget for later renaming
- Builder.unnamedViews += view
- None
}
- // TODO We must also record children as unnamed, some could be namable but this requires changes to the Binding
+
+ val fullResult = elementResult ++ aggregateMappings
+
+ // We need to record any Aggregates that don't have a 1-1 mapping (including the view
+ // itself)
getRecursiveFields.lazily(view, "_").foreach {
- case (agg: Aggregate, _) if agg != view =>
- Builder.unnamedViews += agg
+ case (unnamed: Aggregate, _) if !fullResult.contains(unnamed) =>
+ Builder.unnamedViews += unnamed
case _ => // Do nothing
}
- agg.bind(AggregateViewBinding(resultBindings, topt))
+ agg.bind(AggregateViewBinding(fullResult))
}
}
@@ -208,8 +215,8 @@ package object dataview {
private def unfoldView(elt: Element): LazyList[Element] = {
def rec(e: Element): LazyList[Element] = e.topBindingOpt match {
case Some(ViewBinding(target)) => target #:: rec(target)
- case Some(AggregateViewBinding(mapping, _)) =>
- val target = mapping(e)
+ case Some(avb: AggregateViewBinding) =>
+ val target = avb.lookup(e).get
target #:: rec(target)
case Some(_) | None => LazyList.empty
}
@@ -246,15 +253,11 @@ package object dataview {
*/
private[chisel3] def reifySingleData(data: Data): Option[Data] = {
val candidate: Option[Data] =
- data.binding.collect { // First check if this is a total mapping of an Aggregate
- case AggregateViewBinding(_, Some(t)) => t
- }.orElse { // Otherwise look via top binding
- data.topBindingOpt match {
- case None => None
- case Some(ViewBinding(target)) => Some(target)
- case Some(AggregateViewBinding(lookup, _)) => lookup.get(data)
- case Some(_) => None
- }
+ data.topBindingOpt match {
+ case None => None
+ case Some(ViewBinding(target)) => Some(target)
+ case Some(AggregateViewBinding(lookup)) => lookup.get(data)
+ case Some(_) => None
}
candidate.flatMap { d =>
// Candidate may itself be a view, keep tracing in those cases
diff --git a/core/src/main/scala/chisel3/experimental/hierarchy/Lookupable.scala b/core/src/main/scala/chisel3/experimental/hierarchy/Lookupable.scala
index 60290f83..46a38e7c 100644
--- a/core/src/main/scala/chisel3/experimental/hierarchy/Lookupable.scala
+++ b/core/src/main/scala/chisel3/experimental/hierarchy/Lookupable.scala
@@ -173,12 +173,12 @@ object Lookupable {
// We have to lookup the target(s) of the view since they may need to be underlying into the current context
val newBinding = data.topBinding match {
case ViewBinding(target) => ViewBinding(lookupData(reify(target)))
- case avb @ AggregateViewBinding(map, targetOpt) =>
+ case avb @ AggregateViewBinding(map) =>
data match {
- case _: Element => ViewBinding(lookupData(reify(map(data))))
+ case e: Element => ViewBinding(lookupData(reify(avb.lookup(e).get)))
case _: Aggregate =>
// Provide a 1:1 mapping if possible
- val singleTargetOpt = targetOpt.filter(_ => avb == data.binding.get).flatMap(reifySingleData)
+ val singleTargetOpt = map.get(data).filter(_ => avb == data.binding.get).flatMap(reifySingleData)
singleTargetOpt match {
case Some(singleTarget) => // It is 1:1!
// This is a little tricky because the values in newMap need to point to Elements of newTarget
@@ -187,15 +187,15 @@ object Lookupable {
case (res, from) =>
(res: Data) -> mapRootAndExtractSubField(map(from), _ => newTarget)
}.toMap
- AggregateViewBinding(newMap, Some(newTarget))
+ AggregateViewBinding(newMap + (result -> newTarget))
case None => // No 1:1 mapping so we have to do a flat binding
// Just remap each Element of this aggregate
val newMap = coiterate(result, data).map {
// Upcast res to Data since Maps are invariant in the Key type parameter
- case (res, from) => (res: Data) -> lookupData(reify(map(from)))
+ case (res, from) => (res: Data) -> lookupData(reify(avb.lookup(from).get))
}.toMap
- AggregateViewBinding(newMap, None)
+ AggregateViewBinding(newMap)
}
}
}
@@ -204,8 +204,8 @@ object Lookupable {
// We must also mark non-1:1 and child Aggregates in the view for renaming
newBinding match {
case _: ViewBinding => // Do nothing
- case AggregateViewBinding(_, target) =>
- if (target.isEmpty) {
+ case AggregateViewBinding(childMap) =>
+ if (!childMap.contains(result)) {
Builder.unnamedViews += result
}
// Binding does not capture 1:1 for child aggregates views
diff --git a/core/src/main/scala/chisel3/experimental/package.scala b/core/src/main/scala/chisel3/experimental/package.scala
index ce258a25..9b9c83f4 100644
--- a/core/src/main/scala/chisel3/experimental/package.scala
+++ b/core/src/main/scala/chisel3/experimental/package.scala
@@ -57,6 +57,34 @@ package object experimental {
type Direction = ActualDirection
val Direction = ActualDirection
+ /** The same as [[IO]] except there is no prefix for the name of the val */
+ def FlatIO[T <: Record](gen: => T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = noPrefix {
+ import dataview._
+ def coerceDirection(d: Data) = {
+ import chisel3.{SpecifiedDirection => SD}
+ DataMirror.specifiedDirectionOf(gen) match {
+ case SD.Flip => Flipped(d)
+ case SD.Input => Input(d)
+ case SD.Output => Output(d)
+ case _ => d
+ }
+ }
+ val ports: Seq[Data] =
+ gen.elements.toSeq.reverse.map {
+ case (name, data) =>
+ val p = IO(coerceDirection(chiselTypeClone(data).asInstanceOf[Data]))
+ p.suggestName(name)
+ p
+
+ }
+
+ implicit val dv: DataView[Seq[Data], T] = DataView.mapping(
+ _ => chiselTypeClone(gen).asInstanceOf[T],
+ (seq, rec) => seq.zip(rec.elements.toSeq.reverse).map { case (port, (_, field)) => port -> field }
+ )
+ ports.viewAs[T]
+ }
+
implicit class ChiselRange(val sc: StringContext) extends AnyVal {
import scala.language.experimental.macros
diff --git a/core/src/main/scala/chisel3/internal/BiConnect.scala b/core/src/main/scala/chisel3/internal/BiConnect.scala
index a8b425f5..2d6c9e4a 100644
--- a/core/src/main/scala/chisel3/internal/BiConnect.scala
+++ b/core/src/main/scala/chisel3/internal/BiConnect.scala
@@ -285,7 +285,13 @@ private[chisel3] object BiConnect {
case _ => true
}
- typeCheck && contextCheck && bindingCheck && flow_check && sourceNotLiteralCheck
+ // do not bulk connect the 'io' pseudo-bundle of a BlackBox since it will be decomposed in FIRRTL
+ def blackBoxCheck = Seq(source, sink).map(_._parent).forall {
+ case Some(_: BlackBox) => false
+ case _ => true
+ }
+
+ typeCheck && contextCheck && bindingCheck && flow_check && sourceNotLiteralCheck && blackBoxCheck
}
// These functions (finally) issue the connection operation
diff --git a/core/src/main/scala/chisel3/internal/Binding.scala b/core/src/main/scala/chisel3/internal/Binding.scala
index 6431dd23..bab79bc1 100644
--- a/core/src/main/scala/chisel3/internal/Binding.scala
+++ b/core/src/main/scala/chisel3/internal/Binding.scala
@@ -141,11 +141,16 @@ case class DontCareBinding() extends UnconstrainedBinding
private[chisel3] case class ViewBinding(target: Element) extends UnconstrainedBinding
/** Binding for Aggregate Views
- * @param childMap Mapping from children of this view to each child's target
+ * @param childMap Mapping from children of this view to their respective targets
* @param target Optional Data this Aggregate views if the view is total and the target is a Data
+ * @note For any Elements in the childMap, both key and value must be Elements
+ * @note The types of key and value need not match for the top Data in a total view of type
+ * Aggregate
*/
-private[chisel3] case class AggregateViewBinding(childMap: Map[Data, Element], target: Option[Data])
- extends UnconstrainedBinding
+private[chisel3] case class AggregateViewBinding(childMap: Map[Data, Data]) extends UnconstrainedBinding {
+ // Helper lookup function since types of Elements always match
+ def lookup(key: Element): Option[Element] = childMap.get(key).map(_.asInstanceOf[Element])
+}
/** Binding for Data's returned from accessing an Instance/Definition members, if not readable/writable port */
private[chisel3] case object CrossModuleBinding extends TopBinding {
diff --git a/core/src/main/scala/chisel3/internal/Builder.scala b/core/src/main/scala/chisel3/internal/Builder.scala
index 1ffe54ab..4180f580 100644
--- a/core/src/main/scala/chisel3/internal/Builder.scala
+++ b/core/src/main/scala/chisel3/internal/Builder.scala
@@ -35,7 +35,7 @@ private[chisel3] class Namespace(keywords: Set[String]) {
// TODO what character set does FIRRTL truly support? using ANSI C for now
def legalStart(c: Char) = (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'
def legal(c: Char) = legalStart(c) || (c >= '0' && c <= '9')
- val res = s.filter(legal)
+ val res = if (s.forall(legal)) s else s.filter(legal)
val headOk = (!res.isEmpty) && (leadingDigitOk || legalStart(res.head))
if (headOk) res else s"_$res"
}
@@ -45,12 +45,9 @@ private[chisel3] class Namespace(keywords: Set[String]) {
// leadingDigitOk is for use in fields of Records
def name(elem: String, leadingDigitOk: Boolean = false): String = {
val sanitized = sanitize(elem, leadingDigitOk)
- if (this contains sanitized) {
- name(rename(sanitized))
- } else {
- names(sanitized) = 1
- sanitized
- }
+ val result = if (this.contains(sanitized)) rename(sanitized) else sanitized
+ names(result) = 1
+ result
}
}
@@ -108,17 +105,16 @@ private[chisel3] trait HasId extends InstanceId {
// Contains the seed computed automatically by the compiler plugin
private var auto_seed: Option[String] = None
- // Prefix at time when this class is constructed
- private val construction_prefix: Prefix = Builder.getPrefix
-
- // Prefix when the latest [[suggestSeed]] or [[autoSeed]] is called
- private var prefix_seed: Prefix = Nil
+ // Prefix for use in naming
+ // - Defaults to prefix at time when object is created
+ // - Overridden when [[suggestSeed]] or [[autoSeed]] is called
+ private var naming_prefix: Prefix = Builder.getPrefix
// Post-seed hooks called to carry the suggested seeds to other candidates as needed
- private val suggest_postseed_hooks = scala.collection.mutable.ListBuffer.empty[String => Unit]
+ private var suggest_postseed_hooks: List[String => Unit] = Nil
// Post-seed hooks called to carry the auto seeds to other candidates as needed
- private val auto_postseed_hooks = scala.collection.mutable.ListBuffer.empty[String => Unit]
+ private var auto_postseed_hooks: List[String => Unit] = Nil
/** Takes the last seed suggested. Multiple calls to this function will take the last given seed, unless
* this HasId is a module port (see overridden method in Data.scala).
@@ -136,8 +132,8 @@ private[chisel3] trait HasId extends InstanceId {
// Bypass the overridden behavior of autoSeed in [[Data]], apply autoSeed even to ports
private[chisel3] def forceAutoSeed(seed: String): this.type = {
auto_seed = Some(seed)
- for (hook <- auto_postseed_hooks) { hook(seed) }
- prefix_seed = Builder.getPrefix
+ for (hook <- auto_postseed_hooks.reverse) { hook(seed) }
+ naming_prefix = Builder.getPrefix
this
}
@@ -153,8 +149,8 @@ private[chisel3] trait HasId extends InstanceId {
*/
def suggestName(seed: => String): this.type = {
if (suggested_seed.isEmpty) suggested_seed = Some(seed)
- prefix_seed = Builder.getPrefix
- for (hook <- suggest_postseed_hooks) { hook(seed) }
+ naming_prefix = Builder.getPrefix
+ for (hook <- suggest_postseed_hooks.reverse) { hook(seed) }
this
}
@@ -183,18 +179,21 @@ private[chisel3] trait HasId extends InstanceId {
*/
def buildName(seed: String, prefix: Prefix): String = {
val builder = new StringBuilder()
- prefix.foreach(builder ++= _ + "_")
+ prefix.foreach { p =>
+ builder ++= p
+ builder += '_'
+ }
builder ++= seed
builder.toString
}
if (hasSeed) {
- Some(buildName(seedOpt.get, prefix_seed.reverse))
+ Some(buildName(seedOpt.get, naming_prefix.reverse))
} else {
defaultSeed.map { default =>
defaultPrefix match {
- case Some(p) => buildName(default, p :: construction_prefix.reverse)
- case None => buildName(default, construction_prefix.reverse)
+ case Some(p) => buildName(default, p :: naming_prefix.reverse)
+ case None => buildName(default, naming_prefix.reverse)
}
}
}
@@ -211,8 +210,8 @@ private[chisel3] trait HasId extends InstanceId {
private[chisel3] def hasAutoSeed: Boolean = auto_seed.isDefined
- private[chisel3] def addSuggestPostnameHook(hook: String => Unit): Unit = suggest_postseed_hooks += hook
- private[chisel3] def addAutoPostnameHook(hook: String => Unit): Unit = auto_postseed_hooks += hook
+ private[chisel3] def addSuggestPostnameHook(hook: String => Unit): Unit = suggest_postseed_hooks ::= hook
+ private[chisel3] def addAutoPostnameHook(hook: String => Unit): Unit = auto_postseed_hooks ::= hook
// Uses a namespace to convert suggestion into a true name
// Will not do any naming if the reference already assigned.
@@ -222,6 +221,8 @@ private[chisel3] trait HasId extends InstanceId {
val candidate_name = _computeName(prefix, Some(default)).get
val available_name = namespace.name(candidate_name)
setRef(Ref(available_name))
+ // Clear naming prefix to free memory
+ naming_prefix = Nil
}
private var _ref: Option[Arg] = None
diff --git a/docs/src/cookbooks/cookbook.md b/docs/src/cookbooks/cookbook.md
index ae7c7bf6..b9e5db38 100644
--- a/docs/src/cookbooks/cookbook.md
+++ b/docs/src/cookbooks/cookbook.md
@@ -26,6 +26,7 @@ Please note that these examples make use of [Chisel's scala-style printing](../e
* [How do I unpack a value ("reverse concatenation") like in Verilog?](#how-do-i-unpack-a-value-reverse-concatenation-like-in-verilog)
* [How do I do subword assignment (assign to some bits in a UInt)?](#how-do-i-do-subword-assignment-assign-to-some-bits-in-a-uint)
* [How do I create an optional I/O?](#how-do-i-create-an-optional-io)
+* [How do I create I/O without a prefix?](#how-do-i-create-io-without-a-prefix)
* [How do I minimize the number of bits used in an output vector](#how-do-i-minimize-the-number-of-bits-used-in-an-output-vector)
* Predictable Naming
* [How do I get Chisel to name signals properly in blocks like when/withClockAndReset?](#how-do-i-get-chisel-to-name-signals-properly-in-blocks-like-whenwithclockandreset)
@@ -546,6 +547,50 @@ class ModuleWithOptionalIO(flag: Boolean) extends Module {
}
```
+### How do I create I/O without a prefix?
+
+In most cases, you can simply call `IO` multiple times:
+
+```scala mdoc:silent:reset
+import chisel3._
+
+class MyModule extends Module {
+ val in = IO(Input(UInt(8.W)))
+ val out = IO(Output(UInt(8.W)))
+
+ out := in +% 1.U
+}
+```
+
+```scala mdoc:verilog
+getVerilogString(new MyModule)
+```
+
+If you have a `Bundle` from which you would like to create ports without the
+normal `val` prefix, you can use `FlatIO`:
+
+```scala mdoc:silent:reset
+import chisel3._
+import chisel3.experimental.FlatIO
+
+class MyBundle extends Bundle {
+ val foo = Input(UInt(8.W))
+ val bar = Output(UInt(8.W))
+}
+
+class MyModule extends Module {
+ val io = FlatIO(new MyBundle)
+
+ io.bar := io.foo +% 1.U
+}
+```
+
+Note that `io_` is nowhere to be seen!
+
+```scala mdoc:verilog
+getVerilogString(new MyModule)
+```
+
### How do I minimize the number of bits used in an output vector?
Use inferred width and a `Seq` instead of a `Vec`:
diff --git a/macros/src/main/scala/chisel3/internal/naming/NamingAnnotations.scala b/macros/src/main/scala/chisel3/internal/naming/NamingAnnotations.scala
index 82223d78..b12826fc 100644
--- a/macros/src/main/scala/chisel3/internal/naming/NamingAnnotations.scala
+++ b/macros/src/main/scala/chisel3/internal/naming/NamingAnnotations.scala
@@ -125,7 +125,7 @@ class NamingTransforms(val c: Context) {
q"""
val $contextVar = $globalNamingStack.pushContext()
..$transformedBody
- if($globalNamingStack.length == 1){
+ if($globalNamingStack.length() == 1){
$contextVar.namePrefix("")
}
$globalNamingStack.popReturnContext(this, $contextVar)
diff --git a/plugin/src/main/scala/chisel3/internal/plugin/BundleComponent.scala b/plugin/src/main/scala/chisel3/internal/plugin/BundleComponent.scala
index d768175d..e3ec0a04 100644
--- a/plugin/src/main/scala/chisel3/internal/plugin/BundleComponent.scala
+++ b/plugin/src/main/scala/chisel3/internal/plugin/BundleComponent.scala
@@ -111,7 +111,7 @@ private[plugin] class BundleComponent(val global: Global, arguments: ChiselPlugi
override def transform(tree: Tree): Tree = tree match {
- case bundle: ClassDef if isBundle(bundle.symbol) =>
+ case bundle: ClassDef if isBundle(bundle.symbol) && !bundle.mods.hasFlag(Flag.ABSTRACT) =>
// ==================== Generate _cloneTypeImpl ====================
val (con, params) = getConstructorAndParams(bundle.impl.body)
if (con.isEmpty) {
diff --git a/src/main/scala/chisel3/util/BlackBoxUtils.scala b/src/main/scala/chisel3/util/BlackBoxUtils.scala
index 7c4400f8..579a6307 100644
--- a/src/main/scala/chisel3/util/BlackBoxUtils.scala
+++ b/src/main/scala/chisel3/util/BlackBoxUtils.scala
@@ -4,13 +4,7 @@ package chisel3.util
import chisel3._
import chisel3.experimental.{ChiselAnnotation, RunFirrtlTransform}
-import firrtl.transforms.{
- BlackBoxInlineAnno,
- BlackBoxNotFoundException,
- BlackBoxPathAnno,
- BlackBoxResourceAnno,
- BlackBoxSourceHelper
-}
+import firrtl.transforms.{BlackBoxInlineAnno, BlackBoxNotFoundException, BlackBoxPathAnno, BlackBoxSourceHelper}
import firrtl.annotations.ModuleName
import logger.LazyLogging
diff --git a/src/main/scala/chisel3/util/ExtModuleUtils.scala b/src/main/scala/chisel3/util/ExtModuleUtils.scala
index 5c9c02ba..8a687d36 100644
--- a/src/main/scala/chisel3/util/ExtModuleUtils.scala
+++ b/src/main/scala/chisel3/util/ExtModuleUtils.scala
@@ -3,13 +3,7 @@
package chisel3.util
import chisel3.experimental.{ChiselAnnotation, ExtModule, RunFirrtlTransform}
-import firrtl.transforms.{
- BlackBoxInlineAnno,
- BlackBoxNotFoundException,
- BlackBoxPathAnno,
- BlackBoxResourceAnno,
- BlackBoxSourceHelper
-}
+import firrtl.transforms.{BlackBoxInlineAnno, BlackBoxNotFoundException, BlackBoxPathAnno, BlackBoxSourceHelper}
import BlackBoxHelpers._
diff --git a/src/test/scala/chiselTests/BlackBox.scala b/src/test/scala/chiselTests/BlackBox.scala
index 27cdbbc4..f923a94a 100644
--- a/src/test/scala/chiselTests/BlackBox.scala
+++ b/src/test/scala/chiselTests/BlackBox.scala
@@ -145,6 +145,17 @@ class BlackBoxTypeParam(w: Int, raw: String) extends BlackBox(Map("T" -> RawPara
})
}
+class BlackBoxNoIO extends BlackBox {
+ // Whoops! typo
+ val ioo = IO(new Bundle {
+ val out = Output(UInt(8.W))
+ })
+}
+
+class BlackBoxUIntIO extends BlackBox {
+ val io = IO(Output(UInt(8.W)))
+}
+
class BlackBoxWithParamsTester extends BasicTester {
val blackBoxOne = Module(new BlackBoxConstant(1))
val blackBoxFour = Module(new BlackBoxConstant(4))
@@ -192,7 +203,23 @@ class BlackBoxSpec extends ChiselFlatSpec {
assert(DataMirror.modulePorts(m) == Seq("in" -> m.io.in, "out" -> m.io.out))
})
}
- "A BlackBoxed using suggestName(\"io\")" should "work (but don't do this)" in {
+ "A BlackBox using suggestName(\"io\")" should "work (but don't do this)" in {
assertTesterPasses({ new BlackBoxTesterSuggestName }, Seq("/chisel3/BlackBoxTest.v"), TesterDriver.verilatorOnly)
}
+
+ "A BlackBox with no 'val io'" should "give a reasonable error message" in {
+ (the[ChiselException] thrownBy {
+ ChiselStage.elaborate(new Module {
+ val inst = Module(new BlackBoxNoIO)
+ })
+ }).getMessage should include("must have a port named 'io' of type Record")
+ }
+
+ "A BlackBox with non-Record 'val io'" should "give a reasonable error message" in {
+ (the[ChiselException] thrownBy {
+ ChiselStage.elaborate(new Module {
+ val inst = Module(new BlackBoxUIntIO)
+ })
+ }).getMessage should include("must have a port named 'io' of type Record")
+ }
}
diff --git a/src/test/scala/chiselTests/BulkConnectSpec.scala b/src/test/scala/chiselTests/BulkConnectSpec.scala
index 463122bd..281890d4 100644
--- a/src/test/scala/chiselTests/BulkConnectSpec.scala
+++ b/src/test/scala/chiselTests/BulkConnectSpec.scala
@@ -94,6 +94,26 @@ class BulkConnectSpec extends ChiselPropSpec {
chirrtl should include("deq <= enq")
}
+ property("Chisel connects should not emit a FIRRTL bulk connect for BlackBox IO Bundles") {
+ class MyBundle extends Bundle {
+ val O: Bool = Output(Bool())
+ val I: Bool = Input(Bool())
+ }
+
+ val chirrtl = ChiselStage.emitChirrtl(new Module {
+ val io: MyBundle = IO(Flipped(new MyBundle))
+
+ val bb = Module(new BlackBox {
+ val io: MyBundle = IO(Flipped(new MyBundle))
+ })
+
+ io <> bb.io
+ })
+ // There won't be a bb.io Bundle in FIRRTL, so connections have to be done element-wise
+ chirrtl should include("bb.O <= io.O")
+ chirrtl should include("io.I <= bb.I")
+ }
+
property("MonoConnect should bulk connect undirectioned internal wires") {
val chirrtl = ChiselStage.emitChirrtl(new Module {
val io = IO(new Bundle {})
diff --git a/src/test/scala/chiselTests/InvalidateAPISpec.scala b/src/test/scala/chiselTests/InvalidateAPISpec.scala
index 2c51e5d2..dbd353a0 100644
--- a/src/test/scala/chiselTests/InvalidateAPISpec.scala
+++ b/src/test/scala/chiselTests/InvalidateAPISpec.scala
@@ -228,4 +228,12 @@ class InvalidateAPISpec extends ChiselPropSpec with Matchers with BackendCompila
val firrtlOutput = myGenerateFirrtl(new ModuleWithoutDontCare)
firrtlOutput should include("is invalid")
}
+
+ property("a clock should be able to be connected to a DontCare") {
+ class ClockConnectedToDontCare extends Module {
+ val foo = IO(Output(Clock()))
+ foo := DontCare
+ }
+ myGenerateFirrtl(new ClockConnectedToDontCare) should include("foo is invalid")
+ }
}
diff --git a/src/test/scala/chiselTests/Mem.scala b/src/test/scala/chiselTests/Mem.scala
index 4dcb1ad4..c5fcc6b1 100644
--- a/src/test/scala/chiselTests/Mem.scala
+++ b/src/test/scala/chiselTests/Mem.scala
@@ -3,6 +3,7 @@
package chiselTests
import chisel3._
+import chisel3.stage.ChiselStage
import chisel3.util._
import chisel3.testers.BasicTester
@@ -141,6 +142,52 @@ class MemBundleTester extends BasicTester {
}
}
+private class TrueDualPortMemoryIO(val addrW: Int, val dataW: Int) extends Bundle {
+ require(addrW > 0, "address width must be greater than 0")
+ require(dataW > 0, "data width must be greater than 0")
+
+ val clka = Input(Clock())
+ val ena = Input(Bool())
+ val wea = Input(Bool())
+ val addra = Input(UInt(addrW.W))
+ val dia = Input(UInt(dataW.W))
+ val doa = Output(UInt(dataW.W))
+
+ val clkb = Input(Clock())
+ val enb = Input(Bool())
+ val web = Input(Bool())
+ val addrb = Input(UInt(addrW.W))
+ val dib = Input(UInt(dataW.W))
+ val dob = Output(UInt(dataW.W))
+}
+
+private class TrueDualPortMemory(addrW: Int, dataW: Int) extends RawModule {
+ val io = IO(new TrueDualPortMemoryIO(addrW, dataW))
+ val ram = SyncReadMem(1 << addrW, UInt(dataW.W))
+
+ // Port a
+ withClock(io.clka) {
+ io.doa := DontCare
+ when(io.ena) {
+ when(io.wea) {
+ ram(io.addra) := io.dia
+ }
+ io.doa := ram(io.addra)
+ }
+ }
+
+ // Port b
+ withClock(io.clkb) {
+ io.dob := DontCare
+ when(io.enb) {
+ when(io.web) {
+ ram(io.addrb) := io.dib
+ }
+ io.dob := ram(io.addrb)
+ }
+ }
+}
+
class MemorySpec extends ChiselPropSpec {
property("Mem of Vec should work") {
assertTesterPasses { new MemVecTester }
@@ -186,4 +233,9 @@ class MemorySpec extends ChiselPropSpec {
|}
|""".stripMargin should compile
}
+
+ property("memories in modules without implicit clock should compile without warning or error") {
+ val stage = new ChiselStage
+ stage.emitVerilog(new TrueDualPortMemory(4, 32))
+ }
}
diff --git a/src/test/scala/chiselTests/ReduceTreeSpec.scala b/src/test/scala/chiselTests/ReduceTreeSpec.scala
new file mode 100644
index 00000000..3f078106
--- /dev/null
+++ b/src/test/scala/chiselTests/ReduceTreeSpec.scala
@@ -0,0 +1,106 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package chiselTests
+
+import chisel3._
+import chisel3.util._
+import chisel3.testers.BasicTester
+
+class Arbiter[T <: Data: Manifest](n: Int, private val gen: T) extends Module {
+ val io = IO(new Bundle {
+ val in = Flipped(Vec(n, new DecoupledIO(gen)))
+ val out = new DecoupledIO(gen)
+ })
+
+ def arbitrateTwo(a: DecoupledIO[T], b: DecoupledIO[T]) = {
+
+ val idleA :: idleB :: hasA :: hasB :: Nil = Enum(4)
+ val regData = Reg(gen)
+ val regState = RegInit(idleA)
+ val out = Wire(new DecoupledIO(gen))
+
+ a.ready := regState === idleA
+ b.ready := regState === idleB
+ out.valid := (regState === hasA || regState === hasB)
+
+ switch(regState) {
+ is(idleA) {
+ when(a.valid) {
+ regData := a.bits
+ regState := hasA
+ }.otherwise {
+ regState := idleB
+ }
+ }
+ is(idleB) {
+ when(b.valid) {
+ regData := b.bits
+ regState := hasB
+ }.otherwise {
+ regState := idleA
+ }
+ }
+ is(hasA) {
+ when(out.ready) {
+ regState := idleB
+ }
+ }
+ is(hasB) {
+ when(out.ready) {
+ regState := idleA
+ }
+ }
+ }
+
+ out.bits := regData.asUInt + 1.U
+ out
+ }
+
+ io.out <> io.in.reduceTree(arbitrateTwo)
+}
+
+class ReduceTreeBalancedTester(nodes: Int) extends BasicTester {
+
+ val cnt = RegInit(0.U(8.W))
+ val min = RegInit(99.U(8.W))
+ val max = RegInit(0.U(8.W))
+
+ val dut = Module(new Arbiter(nodes, UInt(16.W)))
+ for (i <- 0 until nodes) {
+ dut.io.in(i).valid := true.B
+ dut.io.in(i).bits := 0.U
+ }
+ dut.io.out.ready := true.B
+
+ when(dut.io.out.valid) {
+ val hops = dut.io.out.bits
+ when(hops < min) {
+ min := hops
+ }
+ when(hops > max) {
+ max := hops
+ }
+ }
+
+ when(!(max === 0.U || min === 99.U)) {
+ assert(max - min <= 1.U)
+ }
+
+ cnt := cnt + 1.U
+ when(cnt === 10.U) {
+ stop()
+ }
+}
+
+class ReduceTreeBalancedSpec extends ChiselPropSpec {
+ property("Tree shall be fair and shall have a maximum difference of one hop for each node") {
+
+ // This test will fail for 5 nodes due to an unbalanced tree.
+ // A fix is on the way.
+ for (n <- 1 to 5) {
+ assertTesterPasses {
+ new ReduceTreeBalancedTester(n)
+ }
+ }
+ }
+}
diff --git a/src/test/scala/chiselTests/experimental/DataView.scala b/src/test/scala/chiselTests/experimental/DataView.scala
index 0285a524..e7caacfd 100644
--- a/src/test/scala/chiselTests/experimental/DataView.scala
+++ b/src/test/scala/chiselTests/experimental/DataView.scala
@@ -332,6 +332,36 @@ class DataViewSpec extends ChiselFlatSpec {
chirrtl should include("dataOut <= vec[addr]")
}
+ it should "support dynamic indexing for Vecs that correspond 1:1 in a view" in {
+ class MyBundle extends Bundle {
+ val foo = Vec(4, UInt(8.W))
+ val bar = UInt(2.W)
+ }
+ implicit val myView = DataView[(Vec[UInt], UInt), MyBundle](
+ _ => new MyBundle,
+ _._1 -> _.foo,
+ _._2 -> _.bar
+ )
+ class MyModule extends Module {
+ val dataIn = IO(Input(UInt(8.W)))
+ val addr = IO(Input(UInt(2.W)))
+ val dataOut = IO(Output(UInt(8.W)))
+
+ val vec = RegInit(0.U.asTypeOf(Vec(4, UInt(8.W))))
+ val addrReg = Reg(UInt(2.W))
+ val view = (vec, addrReg).viewAs[MyBundle]
+ // Dynamic indexing is more of a "generator" in Chisel3 than an individual node
+ // This style is not recommended, this is just testing the behavior
+ val selected = view.foo(view.bar)
+ view.bar := addr
+ selected := dataIn
+ dataOut := selected
+ }
+ val chirrtl = ChiselStage.emitChirrtl(new MyModule)
+ chirrtl should include("vec[addrReg] <= dataIn")
+ chirrtl should include("dataOut <= vec[addrReg]")
+ }
+
it should "error if you try to dynamically index a Vec view that does not correspond to a Vec target" in {
class MyModule extends Module {
val inA, inB = IO(Input(UInt(8.W)))
diff --git a/src/test/scala/chiselTests/experimental/DataViewTargetSpec.scala b/src/test/scala/chiselTests/experimental/DataViewTargetSpec.scala
index da27c9c8..ddeeab6e 100644
--- a/src/test/scala/chiselTests/experimental/DataViewTargetSpec.scala
+++ b/src/test/scala/chiselTests/experimental/DataViewTargetSpec.scala
@@ -125,9 +125,7 @@ class DataViewTargetSpec extends ChiselFlatSpec {
val pairs = annos.collect { case DummyAnno(t, idx) => (idx, t.toString) }.sortBy(_._1)
val expected = Seq(
0 -> "~MyParent|MyChild>out.foo",
- // The child of the view that was itself an Aggregate got split because 1:1 is lacking here
- 1 -> "~MyParent|MyChild>out.foo[0]",
- 1 -> "~MyParent|MyChild>out.foo[1]",
+ 1 -> "~MyParent|MyChild>out.foo",
2 -> "~MyParent|MyParent/inst:MyChild>out.foo",
3 -> "~MyParent|MyParent/inst:MyChild>out"
)
diff --git a/src/test/scala/chiselTests/experimental/FlatIOSpec.scala b/src/test/scala/chiselTests/experimental/FlatIOSpec.scala
new file mode 100644
index 00000000..dfce447f
--- /dev/null
+++ b/src/test/scala/chiselTests/experimental/FlatIOSpec.scala
@@ -0,0 +1,51 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package chiselTests.experimental
+
+import chisel3._
+import chisel3.util.Valid
+import chisel3.stage.ChiselStage.emitChirrtl
+import chisel3.experimental.FlatIO
+import chiselTests.ChiselFlatSpec
+
+class FlatIOSpec extends ChiselFlatSpec {
+ behavior.of("FlatIO")
+
+ it should "create ports without a prefix" in {
+ class MyModule extends RawModule {
+ val io = FlatIO(new Bundle {
+ val in = Input(UInt(8.W))
+ val out = Output(UInt(8.W))
+ })
+ io.out := io.in
+ }
+ val chirrtl = emitChirrtl(new MyModule)
+ chirrtl should include("input in : UInt<8>")
+ chirrtl should include("output out : UInt<8>")
+ chirrtl should include("out <= in")
+ }
+
+ it should "support bulk connections between FlatIOs and regular IOs" in {
+ class MyModule extends RawModule {
+ val in = FlatIO(Input(Valid(UInt(8.W))))
+ val out = IO(Output(Valid(UInt(8.W))))
+ out := in
+ }
+ val chirrtl = emitChirrtl(new MyModule)
+ chirrtl should include("out.bits <= bits")
+ chirrtl should include("out.valid <= valid")
+ }
+
+ it should "dynamically indexing Vecs inside of FlatIOs" in {
+ class MyModule extends RawModule {
+ val io = FlatIO(new Bundle {
+ val addr = Input(UInt(2.W))
+ val in = Input(Vec(4, UInt(8.W)))
+ val out = Output(Vec(4, UInt(8.W)))
+ })
+ io.out(io.addr) := io.in(io.addr)
+ }
+ val chirrtl = emitChirrtl(new MyModule)
+ chirrtl should include("out[addr] <= in[addr]")
+ }
+}