Skip to content

Commit 3a6cc75

Browse files
committed
Merge branch '3.5.x' into 3.5-release
2 parents be1ac06 + d5a964f commit 3a6cc75

28 files changed

+496
-101
lines changed

core/src/main/scala/chisel3/Aggregate.scala

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import chisel3.internal.Builder.pushCommand
1414
import chisel3.internal.firrtl._
1515
import chisel3.internal.sourceinfo._
1616

17+
import java.lang.Math.{floor, log10, pow}
1718
import scala.collection.mutable
1819

1920
class AliasedAggregateFieldException(message: String) extends ChiselException(message)
@@ -381,11 +382,30 @@ sealed class Vec[T <: Data] private[chisel3] (gen: => T, val length: Int) extend
381382
compileOptions: CompileOptions
382383
): T = {
383384
require(!isEmpty, "Cannot apply reduction on a vec of size 0")
384-
var curLayer: Seq[T] = this
385-
while (curLayer.length > 1) {
386-
curLayer = curLayer.grouped(2).map(x => if (x.length == 1) layerOp(x(0)) else redOp(x(0), x(1))).toSeq
385+
386+
def recReduce[T](s: Seq[T], op: (T, T) => T, lop: (T) => T): T = {
387+
388+
val n = s.length
389+
n match {
390+
case 1 => lop(s(0))
391+
case 2 => op(s(0), s(1))
392+
case _ =>
393+
val m = pow(2, floor(log10(n - 1) / log10(2))).toInt // number of nodes in next level, will be a power of 2
394+
val p = 2 * m - n // number of nodes promoted
395+
396+
val l = s.take(p).map(lop)
397+
val r = s
398+
.drop(p)
399+
.grouped(2)
400+
.map {
401+
case Seq(a, b) => op(a, b)
402+
}
403+
.toVector
404+
recReduce(l ++ r, op, lop)
405+
}
387406
}
388-
curLayer(0)
407+
408+
recReduce(this, redOp, layerOp)
389409
}
390410

391411
/** Creates a Vec literal of this type with specified values. this must be a chisel type.

core/src/main/scala/chisel3/Bits.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1324,7 +1324,7 @@ sealed class Bool() extends UInt(1.W) with Reset {
13241324
/** Logical or operator
13251325
*
13261326
* @param that a hardware $coll
1327-
* @return the lgocial or of this $coll and `that`
1327+
* @return the logical or of this $coll and `that`
13281328
* @note this is equivalent to [[Bool!.|(that:chisel3\.Bool)* Bool.|)]]
13291329
* @group Logical
13301330
*/
@@ -1336,7 +1336,7 @@ sealed class Bool() extends UInt(1.W) with Reset {
13361336
/** Logical and operator
13371337
*
13381338
* @param that a hardware $coll
1339-
* @return the lgocial and of this $coll and `that`
1339+
* @return the logical and of this $coll and `that`
13401340
* @note this is equivalent to [[Bool!.&(that:chisel3\.Bool)* Bool.&]]
13411341
* @group Logical
13421342
*/

core/src/main/scala/chisel3/BlackBox.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ abstract class BlackBox(
157157
_compatAutoWrapPorts() // pre-IO(...) compatibility hack
158158

159159
// Restrict IO to just io, clock, and reset
160-
if (!_io.forall(portsContains)) {
160+
if (!_io.exists(portsContains)) {
161161
throwException(s"BlackBox '$this' must have a port named 'io' of type Record wrapped in IO(...)!")
162162
}
163163

core/src/main/scala/chisel3/Clock.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ sealed class Clock(private[chisel3] val width: Width = Width(1)) extends Element
2323

2424
override def connect(that: Data)(implicit sourceInfo: SourceInfo, connectCompileOptions: CompileOptions): Unit =
2525
that match {
26-
case _: Clock => super.connect(that)(sourceInfo, connectCompileOptions)
26+
case _: Clock | DontCare => super.connect(that)(sourceInfo, connectCompileOptions)
2727
case _ => super.badConnect(that)(sourceInfo)
2828
}
2929

core/src/main/scala/chisel3/Data.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -667,7 +667,7 @@ abstract class Data extends HasId with NamedComponent with SourceInfoDoc {
667667
topBindingOpt match {
668668
// DataView
669669
case Some(ViewBinding(target)) => reify(target).ref
670-
case Some(AggregateViewBinding(viewMap, _)) =>
670+
case Some(AggregateViewBinding(viewMap)) =>
671671
viewMap.get(this) match {
672672
case None => materializeWire() // FIXME FIRRTL doesn't have Aggregate Init expressions
673673
// This should not be possible because Element does the lookup in .topBindingOpt

core/src/main/scala/chisel3/Element.scala

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,15 @@ abstract class Element extends Data {
3636
case Some(litArg) => Some(ElementLitBinding(litArg))
3737
case _ => Some(DontCareBinding())
3838
}
39-
case Some(b @ AggregateViewBinding(viewMap, _)) =>
39+
// TODO Do we even need this? Looking up things in the AggregateViewBinding is fine
40+
case Some(b @ AggregateViewBinding(viewMap)) =>
4041
viewMap.get(this) match {
41-
case Some(elt) => Some(ViewBinding(elt))
42-
case _ => throwException(s"Internal Error! $this missing from topBinding $b")
42+
case Some(elt: Element) => Some(ViewBinding(elt))
43+
// TODO We could generate a reduced AggregateViewBinding, but is there a point?
44+
// Generating the new object would be somewhat slow, it's not clear if we should do this
45+
// matching anyway
46+
case Some(data: Aggregate) => Some(b)
47+
case _ => throwException(s"Internal Error! $this missing from topBinding $b")
4348
}
4449
case topBindingOpt => topBindingOpt
4550
}

core/src/main/scala/chisel3/Mem.scala

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,9 @@ sealed abstract class MemBase[T <: Data](val t: T, val length: BigInt)
5656
with SourceInfoDoc {
5757
_parent.foreach(_.addId(this))
5858

59-
private val clockInst: Clock = Builder.forcedClock
59+
// if the memory is created in a scope with an implicit clock (-> clockInst is defined), we will perform checks that
60+
// ensure memory ports are created with the same clock unless explicitly specified to use a different clock
61+
private val clockInst: Option[Clock] = Builder.currentClock
6062

6163
protected def clockWarning(sourceInfo: Option[SourceInfo]): Unit = {
6264
// 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)
132134
implicit sourceInfo: SourceInfo,
133135
compileOptions: CompileOptions
134136
): T = {
135-
if (warn && clock != clockInst) {
137+
if (warn && clockInst.isDefined && clock != clockInst.get) {
136138
clockWarning(Some(sourceInfo))
137139
}
138140
makePort(sourceInfo, idx, dir, clock)
@@ -164,7 +166,7 @@ sealed abstract class MemBase[T <: Data](val t: T, val length: BigInt)
164166
)(
165167
implicit compileOptions: CompileOptions
166168
): Unit = {
167-
if (warn && clock != clockInst) {
169+
if (warn && clockInst.isDefined && clock != clockInst.get) {
168170
clockWarning(None)
169171
}
170172
implicit val sourceInfo = UnlocatableSourceInfo
@@ -223,7 +225,7 @@ sealed abstract class MemBase[T <: Data](val t: T, val length: BigInt)
223225
compileOptions: CompileOptions
224226
): Unit = {
225227
implicit val sourceInfo = UnlocatableSourceInfo
226-
if (warn && clock != clockInst) {
228+
if (warn && clockInst.isDefined && clock != clockInst.get) {
227229
clockWarning(None)
228230
}
229231
val accessor = makePort(sourceInfo, idx, MemPortDirection.WRITE, clock).asInstanceOf[Vec[Data]]

core/src/main/scala/chisel3/Printable.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import java.util.{MissingFormatArgumentException, UnknownFormatConversionExcepti
1818
* }}}
1919
* This is equivalent to writing:
2020
* {{{
21-
* printf(p"The value of wire = %d\n", wire)
21+
* printf("The value of wire = %d\n", wire)
2222
* }}}
2323
* All Chisel data types have a method `.toPrintable` that gives a default pretty print that can be
2424
* accessed via `p"..."`. This works even for aggregate types, for example:

core/src/main/scala/chisel3/VerificationStatement.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,8 @@ object assert {
9292
): Assert = {
9393
val id = new Assert()
9494
when(!Module.reset.asBool()) {
95-
Builder.pushCommand(Verification(id, Formal.Assert, sourceInfo, Module.clock.ref, cond.ref, ""))
9695
failureMessage("Assertion", line, cond, message, data)
96+
Builder.pushCommand(Verification(id, Formal.Assert, sourceInfo, Module.clock.ref, cond.ref, ""))
9797
}
9898
id
9999
}
@@ -178,8 +178,8 @@ object assume {
178178
): Assume = {
179179
val id = new Assume()
180180
when(!Module.reset.asBool()) {
181-
Builder.pushCommand(Verification(id, Formal.Assume, sourceInfo, Module.clock.ref, cond.ref, ""))
182181
failureMessage("Assumption", line, cond, message, data)
182+
Builder.pushCommand(Verification(id, Formal.Assume, sourceInfo, Module.clock.ref, cond.ref, ""))
183183
}
184184
id
185185
}

core/src/main/scala/chisel3/experimental/dataview/package.scala

Lines changed: 29 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -97,11 +97,16 @@ package object dataview {
9797
val targetContains: Data => Boolean = implicitly[DataProduct[T]].dataSet(target)
9898

9999
// Resulting bindings for each Element of the View
100-
val childBindings =
100+
// Kept separate from Aggregates for totality checking
101+
val elementBindings =
101102
new mutable.HashMap[Data, mutable.ListBuffer[Element]] ++
102103
viewFieldLookup.view.collect { case (elt: Element, _) => elt }
103104
.map(_ -> new mutable.ListBuffer[Element])
104105

106+
// Record any Aggregates that correspond 1:1 for reification
107+
// Using Data instead of Aggregate to avoid unnecessary checks
108+
val aggregateMappings = mutable.ArrayBuffer.empty[(Data, Data)]
109+
105110
def viewFieldName(d: Data): String =
106111
viewFieldLookup.get(d).map(_ + " ").getOrElse("") + d.toString
107112

@@ -130,7 +135,7 @@ package object dataview {
130135
s"View field $fieldName has width ${vwidth} that is incompatible with target value $tex's width ${twidth}"
131136
)
132137
}
133-
childBindings(vex) += tex
138+
elementBindings(vex) += tex
134139
}
135140

136141
mapping.foreach {
@@ -145,7 +150,7 @@ package object dataview {
145150
}
146151
getMatchedFields(aa, ba).foreach {
147152
case (aelt: Element, belt: Element) => onElt(aelt, belt)
148-
case _ => // Ignore matching of Aggregates
153+
case (t, v) => aggregateMappings += (v -> t)
149154
}
150155
}
151156

@@ -155,7 +160,7 @@ package object dataview {
155160

156161
val targetSeen: Option[mutable.Set[Data]] = if (total) Some(mutable.Set.empty[Data]) else None
157162

158-
val resultBindings = childBindings.map {
163+
val elementResult = elementBindings.map {
159164
case (data, targets) =>
160165
val targetsx = targets match {
161166
case collection.Seq(target: Element) => target
@@ -183,23 +188,25 @@ package object dataview {
183188
}
184189

185190
view match {
186-
case elt: Element => view.bind(ViewBinding(resultBindings(elt)))
191+
case elt: Element => view.bind(ViewBinding(elementResult(elt)))
187192
case agg: Aggregate =>
188-
// We record total Data mappings to provide a better .toTarget
189-
val topt = target match {
190-
case d: Data if total => Some(d)
193+
// Don't forget the potential mapping of the view to the target!
194+
target match {
195+
case d: Data if total =>
196+
aggregateMappings += (agg -> d)
191197
case _ =>
192-
// Record views that don't have the simpler .toTarget for later renaming
193-
Builder.unnamedViews += view
194-
None
195198
}
196-
// TODO We must also record children as unnamed, some could be namable but this requires changes to the Binding
199+
200+
val fullResult = elementResult ++ aggregateMappings
201+
202+
// We need to record any Aggregates that don't have a 1-1 mapping (including the view
203+
// itself)
197204
getRecursiveFields.lazily(view, "_").foreach {
198-
case (agg: Aggregate, _) if agg != view =>
199-
Builder.unnamedViews += agg
205+
case (unnamed: Aggregate, _) if !fullResult.contains(unnamed) =>
206+
Builder.unnamedViews += unnamed
200207
case _ => // Do nothing
201208
}
202-
agg.bind(AggregateViewBinding(resultBindings, topt))
209+
agg.bind(AggregateViewBinding(fullResult))
203210
}
204211
}
205212

@@ -208,8 +215,8 @@ package object dataview {
208215
private def unfoldView(elt: Element): LazyList[Element] = {
209216
def rec(e: Element): LazyList[Element] = e.topBindingOpt match {
210217
case Some(ViewBinding(target)) => target #:: rec(target)
211-
case Some(AggregateViewBinding(mapping, _)) =>
212-
val target = mapping(e)
218+
case Some(avb: AggregateViewBinding) =>
219+
val target = avb.lookup(e).get
213220
target #:: rec(target)
214221
case Some(_) | None => LazyList.empty
215222
}
@@ -246,15 +253,11 @@ package object dataview {
246253
*/
247254
private[chisel3] def reifySingleData(data: Data): Option[Data] = {
248255
val candidate: Option[Data] =
249-
data.binding.collect { // First check if this is a total mapping of an Aggregate
250-
case AggregateViewBinding(_, Some(t)) => t
251-
}.orElse { // Otherwise look via top binding
252-
data.topBindingOpt match {
253-
case None => None
254-
case Some(ViewBinding(target)) => Some(target)
255-
case Some(AggregateViewBinding(lookup, _)) => lookup.get(data)
256-
case Some(_) => None
257-
}
256+
data.topBindingOpt match {
257+
case None => None
258+
case Some(ViewBinding(target)) => Some(target)
259+
case Some(AggregateViewBinding(lookup)) => lookup.get(data)
260+
case Some(_) => None
258261
}
259262
candidate.flatMap { d =>
260263
// Candidate may itself be a view, keep tracing in those cases

core/src/main/scala/chisel3/experimental/hierarchy/Lookupable.scala

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -173,12 +173,12 @@ object Lookupable {
173173
// We have to lookup the target(s) of the view since they may need to be underlying into the current context
174174
val newBinding = data.topBinding match {
175175
case ViewBinding(target) => ViewBinding(lookupData(reify(target)))
176-
case avb @ AggregateViewBinding(map, targetOpt) =>
176+
case avb @ AggregateViewBinding(map) =>
177177
data match {
178-
case _: Element => ViewBinding(lookupData(reify(map(data))))
178+
case e: Element => ViewBinding(lookupData(reify(avb.lookup(e).get)))
179179
case _: Aggregate =>
180180
// Provide a 1:1 mapping if possible
181-
val singleTargetOpt = targetOpt.filter(_ => avb == data.binding.get).flatMap(reifySingleData)
181+
val singleTargetOpt = map.get(data).filter(_ => avb == data.binding.get).flatMap(reifySingleData)
182182
singleTargetOpt match {
183183
case Some(singleTarget) => // It is 1:1!
184184
// This is a little tricky because the values in newMap need to point to Elements of newTarget
@@ -187,15 +187,15 @@ object Lookupable {
187187
case (res, from) =>
188188
(res: Data) -> mapRootAndExtractSubField(map(from), _ => newTarget)
189189
}.toMap
190-
AggregateViewBinding(newMap, Some(newTarget))
190+
AggregateViewBinding(newMap + (result -> newTarget))
191191

192192
case None => // No 1:1 mapping so we have to do a flat binding
193193
// Just remap each Element of this aggregate
194194
val newMap = coiterate(result, data).map {
195195
// Upcast res to Data since Maps are invariant in the Key type parameter
196-
case (res, from) => (res: Data) -> lookupData(reify(map(from)))
196+
case (res, from) => (res: Data) -> lookupData(reify(avb.lookup(from).get))
197197
}.toMap
198-
AggregateViewBinding(newMap, None)
198+
AggregateViewBinding(newMap)
199199
}
200200
}
201201
}
@@ -204,8 +204,8 @@ object Lookupable {
204204
// We must also mark non-1:1 and child Aggregates in the view for renaming
205205
newBinding match {
206206
case _: ViewBinding => // Do nothing
207-
case AggregateViewBinding(_, target) =>
208-
if (target.isEmpty) {
207+
case AggregateViewBinding(childMap) =>
208+
if (!childMap.contains(result)) {
209209
Builder.unnamedViews += result
210210
}
211211
// Binding does not capture 1:1 for child aggregates views

core/src/main/scala/chisel3/experimental/package.scala

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,34 @@ package object experimental {
5757
type Direction = ActualDirection
5858
val Direction = ActualDirection
5959

60+
/** The same as [[IO]] except there is no prefix for the name of the val */
61+
def FlatIO[T <: Record](gen: => T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = noPrefix {
62+
import dataview._
63+
def coerceDirection(d: Data) = {
64+
import chisel3.{SpecifiedDirection => SD}
65+
DataMirror.specifiedDirectionOf(gen) match {
66+
case SD.Flip => Flipped(d)
67+
case SD.Input => Input(d)
68+
case SD.Output => Output(d)
69+
case _ => d
70+
}
71+
}
72+
val ports: Seq[Data] =
73+
gen.elements.toSeq.reverse.map {
74+
case (name, data) =>
75+
val p = IO(coerceDirection(chiselTypeClone(data).asInstanceOf[Data]))
76+
p.suggestName(name)
77+
p
78+
79+
}
80+
81+
implicit val dv: DataView[Seq[Data], T] = DataView.mapping(
82+
_ => chiselTypeClone(gen).asInstanceOf[T],
83+
(seq, rec) => seq.zip(rec.elements.toSeq.reverse).map { case (port, (_, field)) => port -> field }
84+
)
85+
ports.viewAs[T]
86+
}
87+
6088
implicit class ChiselRange(val sc: StringContext) extends AnyVal {
6189

6290
import scala.language.experimental.macros

core/src/main/scala/chisel3/internal/BiConnect.scala

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,13 @@ private[chisel3] object BiConnect {
285285
case _ => true
286286
}
287287

288-
typeCheck && contextCheck && bindingCheck && flow_check && sourceNotLiteralCheck
288+
// do not bulk connect the 'io' pseudo-bundle of a BlackBox since it will be decomposed in FIRRTL
289+
def blackBoxCheck = Seq(source, sink).map(_._parent).forall {
290+
case Some(_: BlackBox) => false
291+
case _ => true
292+
}
293+
294+
typeCheck && contextCheck && bindingCheck && flow_check && sourceNotLiteralCheck && blackBoxCheck
289295
}
290296

291297
// These functions (finally) issue the connection operation

0 commit comments

Comments
 (0)