1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
|
// SPDX-License-Identifier: Apache-2.0
package firrtl
package transforms
import firrtl.PrimOps._
import firrtl.annotations._
import firrtl.ir.{AsyncResetType, _}
import firrtl.options.Dependency
import scala.collection.mutable
object PropagatePresetAnnotations {
@deprecated("This message will be removed in the next release.", "FIRRTL 1.5")
val advice =
"Please Note that a Preset-annotated AsyncReset shall NOT be casted to other types with any of the following functions: asInterval, asUInt, asSInt, asClock, asFixedPoint, asAsyncReset."
@deprecated("This exception will no longer be thrown.", "FIRRTL 1.5")
case class TreeCleanUpOrphanException(message: String)
extends FirrtlUserException(s"Node left an orphan during tree cleanup: $message $advice")
}
/** Propagate PresetAnnotations to all children of targeted AsyncResets
* Leaf Registers are annotated with PresetRegAnnotation
* All wires, nodes and connectors along the way are suppressed
*
* Processing of multiples targets are NOT isolated from one another as the expected outcome does not differ
* Annotations of leaf registers, wires, nodes & connectors does indeed not depend on the initial AsyncReset reference
* The set of created annotation based on multiple initial AsyncReset PresetAnnotation
*
* This transform consists of 2 successive walk of the AST
* I./ Propagate
* - 1./ Create all AsyncResetTrees
* - 2./ Leverage them to annotate register for specialized emission & PresetTree for cleanUp
* II./ CleanUpTree
* - clean up all the intermediate nodes (replaced with EmptyStmt)
* - raise Error on orphans (typically cast of Annotated Reset)
* - disconnect Registers from their reset nodes (replaced with UInt(0))
*
* Thanks to the clean-up phase, this transform does not rely on DCE
*
* @note This pass must run before InlineCastsTransform
*/
class PropagatePresetAnnotations extends Transform with DependencyAPIMigration {
override def prerequisites = firrtl.stage.Forms.LowForm :+
Dependency(firrtl.passes.RemoveValidIf)
override def optionalPrerequisites = firrtl.stage.Forms.LowFormOptimized
override def optionalPrerequisiteOf = Seq.empty
override def invalidates(a: Transform) = false
import PropagatePresetAnnotations._
private type TargetSet = mutable.HashSet[ReferenceTarget]
private type TargetMap = mutable.HashMap[ReferenceTarget, String]
private type TargetSetMap = mutable.HashMap[ReferenceTarget, TargetSet]
private val toCleanUp = new TargetSet()
/**
* Logic of the propagation, divided in two main phases:
* 1./ Walk all the Circuit looking for annotated AsyncResets :
* - Store all Annotated AsyncReset reference
* - Build all AsyncReset Trees (whether annotated or not)
* - Store all async-reset-registers (whether annotated or not)
* 2./ Walk the AsyncReset Tree based on Annotated AsyncReset as entry points
* - Annotate all leaf register with PresetRegAnnotation
* - Annotate all intermediate wire, node, connect with PresetConnectorAnnotation
*
* @param cs the circuit state
* @param presetAnnos all the annotations
* @return updated annotations
*/
private def propagate(
cs: CircuitState,
presetAnnos: Seq[PresetAnnotation],
otherAnnos: Seq[Annotation]
): AnnotationSeq = {
val presets = presetAnnos.groupBy(_.target)
// store all annotated asyncreset references
val asyncToAnnotate = new TargetSet()
// store all async-reset-registers
val asyncRegMap = new TargetSetMap()
// store async-reset trees
val asyncCoMap = new TargetSetMap()
// Annotations to be appended and returned as result of the transform
val newAnnos = mutable.ArrayBuffer[Annotation]()
val circuitTarget = CircuitTarget(cs.circuit.main)
/*
* WALK I PHASE 1 FUNCTIONS
*/
/* Walk current module
* - process ports
* - store connections & entry points for PHASE 2
* - process statements
* - Instances => record local instances for cross module AsyncReset Tree Buidling
* - Registers => store AsyncReset bound registers for PHASE 2
* - Wire => store AsyncReset Connections & entry points for PHASE 2
* - Connect => store AsyncReset Connections & entry points for PHASE 2
*
* @param m module
*/
def processModule(m: DefModule): Unit = {
val moduleTarget = circuitTarget.module(m.name)
val localInstances = new TargetMap()
/* Recursively process a given type
* Recursive on Bundle and Vector Type only
* Store Register and Connections for AsyncResetType
* @param tpe [[Type]] to be processed
* @param target [[ReferenceTarget]] associated to the tpe
* @param all Boolean indicating whether all subelements of the current
* tpe should also be stored as Annotated AsyncReset entry points
*/
def processType(tpe: Type, target: ReferenceTarget, all: Boolean): Unit = {
if (tpe == AsyncResetType) {
asyncRegMap(target) = new TargetSet()
asyncCoMap(target) = new TargetSet()
if (presets.contains(target) || all) {
asyncToAnnotate += target
}
} else {
tpe match {
case b: BundleType =>
b.fields.foreach { (x: Field) =>
val tar = target.field(x.name)
processType(x.tpe, tar, (presets.contains(tar) || all))
}
case v: VectorType =>
for (i <- 0 until v.size) {
val tar = target.index(i)
processType(v.tpe, tar, (presets.contains(tar) || all))
}
case _ =>
}
}
}
def processWire(w: DefWire): Unit = {
val target = moduleTarget.ref(w.name)
processType(w.tpe, target, presets.contains(target))
}
/* Recursively search for the ReferenceTarget of a given Expression
* @param e Targeted Expression
* @param ta Local ReferenceTarget of the Targeted Expression
* @return a ReferenceTarget in case of success, a GenericTarget otherwise
* @throws [[InternalError]] on unexpected recursive path return results
*/
def getRef(e: Expression, ta: ReferenceTarget, annoCo: Boolean = false): Target = {
e match {
case w: WRef => moduleTarget.ref(w.name)
case w: WSubField =>
getRef(w.expr, ta, annoCo) match {
case rt: ReferenceTarget =>
if (localInstances.contains(rt)) {
val remote_ref = circuitTarget.module(localInstances(rt))
if (annoCo)
asyncCoMap(ta) += rt.field(w.name)
remote_ref.ref(w.name)
} else {
rt.field(w.name)
}
case remote_target => remote_target
}
case w: WSubIndex =>
getRef(w.expr, ta, annoCo) match {
case remote_target: ReferenceTarget =>
if (annoCo)
asyncCoMap(ta) += remote_target
remote_target.index(w.value)
case _ => Utils.throwInternalError("Unexpected Reference kind")
}
case _ => Target(None, None, Seq.empty)
}
}
def processRegister(r: DefRegister): Unit = {
getRef(r.reset, moduleTarget.ref(r.name), false) match {
case rt: ReferenceTarget =>
if (asyncRegMap.contains(rt)) {
asyncRegMap(rt) += moduleTarget.ref(r.name)
}
case _ =>
}
}
def processConnect(c: Connect): Unit = {
getRef(c.expr, ReferenceTarget("", "", Seq.empty, "", Seq.empty)) match {
case rhs: ReferenceTarget =>
if (presets.contains(rhs) || asyncRegMap.contains(rhs)) {
getRef(c.loc, rhs, true) match {
case lhs: ReferenceTarget =>
if (asyncRegMap.contains(rhs)) {
asyncRegMap(rhs) += lhs
} else {
asyncToAnnotate += lhs
}
case _ => //
}
}
case rhs: GenericTarget => //nothing to do
case _ => Utils.throwInternalError("Unexpected Reference kind")
}
}
def processNode(n: DefNode): Unit = {
val target = moduleTarget.ref(n.name)
processType(n.value.tpe, target, presets.contains(target))
getRef(n.value, ReferenceTarget("", "", Seq.empty, "", Seq.empty)) match {
case rhs: ReferenceTarget =>
if (presets.contains(rhs) || asyncRegMap.contains(rhs)) {
if (asyncRegMap.contains(rhs)) {
asyncRegMap(rhs) += target
} else {
asyncToAnnotate += target
}
}
case rhs: GenericTarget => //nothing to do
case _ => Utils.throwInternalError("Unexpected Reference kind")
}
}
def processStatements(statement: Statement): Unit = {
statement match {
case i: WDefInstance =>
localInstances(moduleTarget.ref(i.name)) = i.module
case r: DefRegister => processRegister(r)
case w: DefWire => processWire(w)
case n: DefNode => processNode(n)
case c: Connect => processConnect(c)
case s => s.foreachStmt(processStatements)
}
}
def processPorts(port: Port): Unit = {
if (port.tpe == AsyncResetType) {
val target = moduleTarget.ref(port.name)
asyncRegMap(target) = new TargetSet()
asyncCoMap(target) = new TargetSet()
if (presets.contains(target)) {
asyncToAnnotate += target
toCleanUp += target
}
}
}
m match {
case module: firrtl.ir.Module =>
module.foreachPort(processPorts)
processStatements(module.body)
case _ =>
}
}
/*
* WALK I PHASE 2 FUNCTIONS
*/
/** Annotate a given target and all its children according to the asyncCoMap */
def annotateCo(ta: ReferenceTarget): Unit = {
if (asyncCoMap.contains(ta)) {
toCleanUp += ta
asyncCoMap(ta).foreach((t: ReferenceTarget) => {
toCleanUp += t
})
}
}
/** Annotate all registers somehow connected to the orignal annotated async reset */
def annotateRegSet(set: TargetSet): Unit = {
set.foreach((ta: ReferenceTarget) => {
annotateCo(ta)
if (asyncRegMap.contains(ta)) {
annotateRegSet(asyncRegMap(ta))
} else {
newAnnos += PresetRegAnnotation(ta)
}
})
}
/**
* Walk AsyncReset Trees with all Annotated AsyncReset as entry points
* Annotate all leaf registers and intermediate wires, nodes, connectors along the way
*/
def annotateAsyncSet(set: TargetSet): Unit = {
set.foreach((t: ReferenceTarget) => {
annotateCo(t)
if (asyncRegMap.contains(t))
annotateRegSet(asyncRegMap(t))
})
}
/*
* MAIN
*/
cs.circuit.foreachModule(processModule) // PHASE 1 : Initialize
annotateAsyncSet(asyncToAnnotate) // PHASE 2 : Annotate
otherAnnos ++ newAnnos
}
/*
* WALK II FUNCTIONS
*/
/**
* Clean-up useless reset tree (not relying on DCE)
* Disconnect preset registers from their reset tree
*/
private def cleanUpPresetTree(circuit: Circuit, annos: AnnotationSeq): Circuit = {
val presetRegs = annos.collect { case a: PresetRegAnnotation => a }.groupBy(_.target)
val circuitTarget = CircuitTarget(circuit.main)
def processModule(m: DefModule): DefModule = {
val moduleTarget = circuitTarget.module(m.name)
val localInstances = new TargetMap()
def getRef(e: Expression): Target = {
e match {
case w: WRef => moduleTarget.ref(w.name)
case w: WSubField =>
getRef(w.expr) match {
case rt: ReferenceTarget =>
if (localInstances.contains(rt)) {
circuitTarget.module(localInstances(rt)).ref(w.name)
} else {
rt.field(w.name)
}
case remote_target => remote_target
}
case w: WSubIndex =>
getRef(w.expr) match {
case remote_target: ReferenceTarget => remote_target.index(w.value)
case _ => Utils.throwInternalError("Unexpected Reference kind")
}
case DoPrim(op, args, _, _) =>
op match {
case AsInterval | AsUInt | AsSInt | AsClock | AsFixedPoint | AsAsyncReset => getRef(args.head)
case _ => Target(None, None, Seq.empty)
}
case _ => Target(None, None, Seq.empty)
}
}
def processRegister(r: DefRegister): DefRegister = {
if (presetRegs.contains(moduleTarget.ref(r.name))) {
r.copy(reset = UIntLiteral(0))
} else {
r
}
}
def processWire(w: DefWire): Statement = {
if (toCleanUp.contains(moduleTarget.ref(w.name))) {
EmptyStmt
} else {
w
}
}
// replaces all references to removed (clean up) preset signals with zero
def replaceCleanUpRefs(e: ir.Expression): ir.Expression = e match {
case r: ir.RefLikeExpression =>
getRef(r) match {
case rt: ReferenceTarget if toCleanUp.contains(rt) =>
Utils.getGroundZero(r.tpe.asInstanceOf[ir.GroundType])
case _ => r.mapExpr(replaceCleanUpRefs)
}
case other => other.mapExpr(replaceCleanUpRefs)
}
def processNode(n: DefNode): Statement = {
if (toCleanUp.contains(moduleTarget.ref(n.name))) {
EmptyStmt
} else {
n
}
}
def processConnect(c: Connect): Statement = {
getRef(c.expr) match {
case rhs: ReferenceTarget if (toCleanUp.contains(rhs)) =>
getRef(c.loc) match {
case lhs: ReferenceTarget if (!toCleanUp.contains(lhs)) =>
c
case _ => EmptyStmt
}
case _ => c
}
}
def processInstance(i: WDefInstance): WDefInstance = {
localInstances(moduleTarget.ref(i.name)) = i.module
val tpe = i.tpe match {
case b: BundleType =>
val inst = moduleTarget.instOf(i.name, i.module).asReference
BundleType(b.fields.filterNot(p => toCleanUp.contains(inst.field(p.name))))
case other => other
}
i.copy(tpe = tpe)
}
def processStatements(statement: Statement): Statement = {
(statement match {
case i: WDefInstance => processInstance(i)
case r: DefRegister => processRegister(r)
case w: DefWire => processWire(w)
case n: DefNode => processNode(n)
case c: Connect => processConnect(c)
case s => s.mapStmt(processStatements)
}).mapExpr(replaceCleanUpRefs)
}
m match {
case module: firrtl.ir.Module =>
val ports = module.ports.filterNot(p => toCleanUp.contains(moduleTarget.ref(p.name)))
module.copy(body = processStatements(module.body), ports = ports)
case _ => m
}
}
circuit.mapModule(processModule)
}
def execute(state: CircuitState): CircuitState = {
// Collect all user-defined PresetAnnotation
val (presets, otherAnnos) = state.annotations.partition { case _: PresetAnnotation => true; case _ => false }
// No PresetAnnotation => no need to walk the IR
if (presets.isEmpty) {
state
} else {
// PHASE I - Propagate
val annos = propagate(state, presets.asInstanceOf[Seq[PresetAnnotation]], otherAnnos)
// PHASE II - CleanUp
val cleanCircuit = cleanUpPresetTree(state.circuit, annos)
// Because toCleanup is a class field, we need to clear it
// TODO refactor so that toCleanup is not a class field
toCleanUp.clear()
state.copy(annotations = annos, circuit = cleanCircuit)
}
}
}
|