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
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
|
// See LICENSE for license details.
package firrtl
import logger._
import java.io.Writer
import scala.collection.mutable
import firrtl.annotations._
import firrtl.ir.Circuit
import firrtl.Utils.throwInternalError
import firrtl.annotations.transforms.{EliminateTargetPaths, ResolvePaths}
import firrtl.options.{DependencyAPI, Dependency, PreservesAll, StageUtils, TransformLike}
import firrtl.stage.transforms.CatchCustomTransformExceptions
/** Container of all annotations for a Firrtl compiler */
class AnnotationSeq private (private[firrtl] val underlying: List[Annotation]) {
def toSeq: Seq[Annotation] = underlying.toSeq
}
object AnnotationSeq {
def apply(xs: Seq[Annotation]): AnnotationSeq = new AnnotationSeq(xs.toList)
}
/** Current State of the Circuit
*
* @constructor Creates a CircuitState object
* @param circuit The current state of the Firrtl AST
* @param form The current form of the circuit
* @param annotations The current collection of [[firrtl.annotations.Annotation Annotation]]
* @param renames A map of [[firrtl.annotations.Named Named]] things that have been renamed.
* Generally only a return value from [[Transform]]s
*/
case class CircuitState(
circuit: Circuit,
form: CircuitForm,
annotations: AnnotationSeq,
renames: Option[RenameMap]) {
/** Helper for getting just an emitted circuit */
def emittedCircuitOption: Option[EmittedCircuit] =
emittedComponents collectFirst { case x: EmittedCircuit => x }
/** Helper for getting an [[EmittedCircuit]] when it is known to exist */
def getEmittedCircuit: EmittedCircuit = emittedCircuitOption match {
case Some(emittedCircuit) => emittedCircuit
case None =>
throw new FirrtlInternalException(s"No EmittedCircuit found! Did you delete any annotations?\n$deletedAnnotations")
}
/** Helper function for extracting emitted components from annotations */
def emittedComponents: Seq[EmittedComponent] =
annotations.collect { case emitted: EmittedAnnotation[_] => emitted.value }
def deletedAnnotations: Seq[Annotation] =
annotations.collect { case anno: DeletedAnnotation => anno }
/** Returns a new CircuitState with all targets being resolved.
* Paths through instances are replaced with a uniquified final target
* Includes modifying the circuit and annotations
* @param targets
* @return
*/
def resolvePaths(targets: Seq[CompleteTarget]): CircuitState = targets match {
case Nil => this
case _ =>
val newCS = new EliminateTargetPaths().runTransform(this.copy(annotations = ResolvePaths(targets) +: annotations ))
newCS.copy(form = form)
}
/** Returns a new CircuitState with the targets of every annotation of a type in annoClasses
* @param annoClasses
* @return
*/
def resolvePathsOf(annoClasses: Class[_]*): CircuitState = {
val targets = getAnnotationsOf(annoClasses:_*).flatMap(_.getTargets)
if(targets.nonEmpty) resolvePaths(targets.flatMap{_.getComplete}) else this
}
/** Returns all annotations which are of a class in annoClasses
* @param annoClasses
* @return
*/
def getAnnotationsOf(annoClasses: Class[_]*): AnnotationSeq = {
annotations.collect { case a if annoClasses.contains(a.getClass) => a }
}
}
object CircuitState {
def apply(circuit: Circuit, form: CircuitForm): CircuitState = apply(circuit, form, Seq())
def apply(circuit: Circuit, form: CircuitForm, annotations: AnnotationSeq): CircuitState =
new CircuitState(circuit, form, annotations, None)
}
/** Current form of the Firrtl Circuit
*
* Form is a measure of addition restrictions on the legality of a Firrtl
* circuit. There is a notion of "highness" and "lowness" implemented in the
* compiler by extending scala.math.Ordered. "Lower" forms add additional
* restrictions compared to "higher" forms. This means that "higher" forms are
* strictly supersets of the "lower" forms. Thus, that any transform that
* operates on [[HighForm]] can also operate on [[MidForm]] or [[LowForm]]
*/
@deprecated("CircuitForm will be removed in 1.3. Switch to Seq[TransformDependency] to specify dependencies.", "1.2")
sealed abstract class CircuitForm(private val value: Int) extends Ordered[CircuitForm] {
// Note that value is used only to allow comparisons
def compare(that: CircuitForm): Int = this.value - that.value
/** Defines a suffix to use if this form is written to a file */
def outputSuffix: String
}
// scalastyle:off magic.number
// These magic numbers give an ordering to CircuitForm
/** Chirrtl Form
*
* The form of the circuit emitted by Chisel. Not a true Firrtl form.
* Includes cmem, smem, and mport IR nodes which enable declaring memories
* separately form their ports. A "Higher" form than [[HighForm]]
*
* See [[CDefMemory]] and [[CDefMPort]]
*/
@deprecated("Form-based dependencies will be removed in 1.3. Please migrate to the new Dependency API.", "1.2")
final case object ChirrtlForm extends CircuitForm(value = 3) {
val outputSuffix: String = ".fir"
}
/** High Form
*
* As detailed in the Firrtl specification
* [[https://github.com/ucb-bar/firrtl/blob/master/spec/spec.pdf]]
*
* Also see [[firrtl.ir]]
*/
@deprecated("Form-based dependencies will be removed in 1.3. Please migrate to the new Dependency API.", "1.2")
final case object HighForm extends CircuitForm(2) {
val outputSuffix: String = ".hi.fir"
}
/** Middle Form
*
* A "lower" form than [[HighForm]] with the following restrictions:
* - All widths must be explicit
* - All whens must be removed
* - There can only be a single connection to any element
*/
@deprecated("Form-based dependencies will be removed in 1.3. Please migrate to the new Dependency API.", "1.2")
final case object MidForm extends CircuitForm(1) {
val outputSuffix: String = ".mid.fir"
}
/** Low Form
*
* The "lowest" form. In addition to the restrictions in [[MidForm]]:
* - All aggregate types (vector/bundle) must have been removed
* - All implicit truncations must be made explicit
*/
@deprecated("Form-based dependencies will be removed in 1.3. Please migrate to the new Dependency API.", "1.2")
final case object LowForm extends CircuitForm(0) {
val outputSuffix: String = ".lo.fir"
}
/** Unknown Form
*
* Often passes may modify a circuit (e.g. InferTypes), but return
* a circuit in the same form it was given.
*
* For this use case, use UnknownForm. It cannot be compared against other
* forms.
*
* TODO(azidar): Replace with PreviousForm, which more explicitly encodes
* this requirement.
*/
@deprecated("Form-based dependencies will be removed in 1.3. Please migrate to the new Dependency API.", "1.2")
final case object UnknownForm extends CircuitForm(-1) {
override def compare(that: CircuitForm): Int = { sys.error("Illegal to compare UnknownForm"); 0 }
val outputSuffix: String = ".unknown.fir"
}
// scalastyle:on magic.number
/** The basic unit of operating on a Firrtl AST */
trait Transform extends TransformLike[CircuitState] with DependencyAPI[Transform] {
/** A convenience function useful for debugging and error messages */
def name: String = this.getClass.getName
/** The [[firrtl.CircuitForm]] that this transform requires to operate on */
@deprecated(
"InputForm/OutputForm will be removed in 1.3. Use DependencyAPI methods (prerequisites, dependents, invalidates)",
"1.2")
def inputForm: CircuitForm
/** The [[firrtl.CircuitForm]] that this transform outputs */
@deprecated(
"InputForm/OutputForm will be removed in 1.3. Use DependencyAPI methods (prerequisites, dependents, invalidates)",
"1.2")
def outputForm: CircuitForm
/** Perform the transform, encode renaming with RenameMap, and can
* delete annotations
* Called by [[runTransform]].
*
* @param state Input Firrtl AST
* @return A transformed Firrtl AST
*/
protected def execute(state: CircuitState): CircuitState
def transform(state: CircuitState): CircuitState = execute(state)
import firrtl.{ChirrtlForm => C, HighForm => H, MidForm => M, LowForm => L, UnknownForm => U}
import firrtl.stage.Forms
override def prerequisites: Seq[Dependency[Transform]] = inputForm match {
case C => Nil
case H => Forms.Deduped
case M => Forms.MidForm
case L => Forms.LowForm
case U => Nil
}
override def optionalPrerequisites: Seq[Dependency[Transform]] = inputForm match {
case L => Forms.LowFormOptimized
case _ => Seq.empty
}
private lazy val fullCompilerSet = new mutable.LinkedHashSet[Dependency[Transform]] ++ Forms.VerilogOptimized
override def dependents: Seq[Dependency[Transform]] = {
val lowEmitters = Dependency[LowFirrtlEmitter] :: Dependency[VerilogEmitter] :: Dependency[MinimumVerilogEmitter] ::
Dependency[SystemVerilogEmitter] :: Nil
val emitters = inputForm match {
case C => Dependency[ChirrtlEmitter] :: Dependency[HighFirrtlEmitter] :: Dependency[MiddleFirrtlEmitter] :: lowEmitters
case H => Dependency[HighFirrtlEmitter] :: Dependency[MiddleFirrtlEmitter] :: lowEmitters
case M => Dependency[MiddleFirrtlEmitter] :: lowEmitters
case L => lowEmitters
case U => Nil
}
val selfDep = Dependency.fromTransform(this)
inputForm match {
case C => (fullCompilerSet ++ emitters - selfDep).toSeq
case H => (fullCompilerSet -- Forms.Deduped ++ emitters - selfDep).toSeq
case M => (fullCompilerSet -- Forms.MidForm ++ emitters - selfDep).toSeq
case L => (fullCompilerSet -- Forms.LowFormOptimized ++ emitters - selfDep).toSeq
case U => Nil
}
}
private lazy val highOutputInvalidates = fullCompilerSet -- Forms.MinimalHighForm
private lazy val midOutputInvalidates = fullCompilerSet -- Forms.MidForm
override def invalidates(a: Transform): Boolean = {
(inputForm, outputForm) match {
case (U, _) | (_, U) => true // invalidate everything
case (i, o) if i >= o => false // invalidate nothing
case (_, C) => true // invalidate everything
case (_, H) => highOutputInvalidates(Dependency.fromTransform(a))
case (_, M) => midOutputInvalidates(Dependency.fromTransform(a))
case (_, L) => false // invalidate nothing
}
}
/** Convenience method to get annotations relevant to this Transform
*
* @param state The [[CircuitState]] form which to extract annotations
* @return A collection of annotations
*/
@deprecated("Just collect the actual Annotation types the transform wants", "1.1")
final def getMyAnnotations(state: CircuitState): Seq[Annotation] = {
val msg = "getMyAnnotations is deprecated, use collect and match on concrete types"
StageUtils.dramaticWarning(msg)
state.annotations.collect { case a: LegacyAnnotation if a.transform == this.getClass => a }
}
/** Executes before any transform's execute method
* @param state
* @return
*/
private[firrtl] def prepare(state: CircuitState): CircuitState = state
/** Perform the transform and update annotations.
*
* @param state Input Firrtl AST
* @return A transformed Firrtl AST
*/
final def runTransform(state: CircuitState): CircuitState = {
logger.info(s"======== Starting Transform $name ========")
val (timeMillis, result) = Utils.time { execute(prepare(state)) }
logger.info(s"""----------------------------${"-" * name.size}---------\n""")
logger.info(f"Time: $timeMillis%.1f ms")
val remappedAnnotations = propagateAnnotations(state.annotations, result.annotations, result.renames)
logger.info(s"Form: ${result.form}")
logger.trace(s"Annotations:")
logger.trace(JsonProtocol.serialize(remappedAnnotations))
logger.trace(s"Circuit:\n${result.circuit.serialize}")
logger.info(s"======== Finished Transform $name ========\n")
CircuitState(result.circuit, result.form, remappedAnnotations, None)
}
/** Propagate annotations and update their names.
*
* @param inAnno input AnnotationSeq
* @param resAnno result AnnotationSeq
* @param renameOpt result RenameMap
* @return the updated annotations
*/
final private def propagateAnnotations(
inAnno: AnnotationSeq,
resAnno: AnnotationSeq,
renameOpt: Option[RenameMap]): AnnotationSeq = {
val newAnnotations = {
val inSet = mutable.LinkedHashSet() ++ inAnno
val resSet = mutable.LinkedHashSet() ++ resAnno
val deleted = (inSet -- resSet).map {
case DeletedAnnotation(xFormName, delAnno) => DeletedAnnotation(s"$xFormName+$name", delAnno)
case anno => DeletedAnnotation(name, anno)
}
val created = resSet -- inSet
val unchanged = resSet & inSet
(deleted ++ created ++ unchanged)
}
// For each annotation, rename all annotations.
val renames = renameOpt.getOrElse(RenameMap())
val remapped2original = mutable.LinkedHashMap[Annotation, mutable.LinkedHashSet[Annotation]]()
val keysOfNote = mutable.LinkedHashSet[Annotation]()
val finalAnnotations = newAnnotations.flatMap { anno =>
val remappedAnnos = anno.update(renames)
remappedAnnos.foreach { remapped =>
val set = remapped2original.getOrElseUpdate(remapped, mutable.LinkedHashSet.empty[Annotation])
set += anno
if(set.size > 1) keysOfNote += remapped
}
remappedAnnos
}.toSeq
keysOfNote.foreach { key =>
logger.debug(s"""The following original annotations are renamed to the same new annotation.""")
logger.debug(s"""Original Annotations:\n ${remapped2original(key).mkString("\n ")}""")
logger.debug(s"""New Annotation:\n $key""")
}
finalAnnotations
}
}
trait SeqTransformBased {
def transforms: Seq[Transform]
protected def runTransforms(state: CircuitState): CircuitState =
transforms.foldLeft(state) { (in, xform) => xform.runTransform(in) }
}
/** For transformations that are simply a sequence of transforms */
abstract class SeqTransform extends Transform with SeqTransformBased {
def execute(state: CircuitState): CircuitState = {
/*
require(state.form <= inputForm,
s"[$name]: Input form must be lower or equal to $inputForm. Got ${state.form}")
*/
val ret = runTransforms(state)
CircuitState(ret.circuit, outputForm, ret.annotations, ret.renames)
}
}
/** Extend for transforms that require resolved targets in their annotations
* Ensures all targets in annotations of a class in annotationClasses are resolved before the execute method
*/
trait ResolvedAnnotationPaths {
this: Transform =>
val annotationClasses: Traversable[Class[_]]
override def prepare(state: CircuitState): CircuitState = {
state.resolvePathsOf(annotationClasses.toSeq:_*)
}
}
/** Defines old API for Emission. Deprecated */
trait Emitter extends Transform with PreservesAll[Transform] {
@deprecated("Use emission annotations instead", "firrtl 1.0")
def emit(state: CircuitState, writer: Writer): Unit
/** An output suffix to use if the output of this [[Emitter]] was written to a file */
def outputSuffix: String
}
object CompilerUtils extends LazyLogging {
/** Generates a sequence of [[Transform]]s to lower a Firrtl circuit
*
* @param inputForm [[CircuitForm]] to lower from
* @param outputForm [[CircuitForm]] to lower to
* @return Sequence of transforms that will lower if outputForm is lower than inputForm
*/
@deprecated("Use a TransformManager requesting which transforms you want to run. This will be removed in 1.3.", "1.2")
def getLoweringTransforms(inputForm: CircuitForm, outputForm: CircuitForm): Seq[Transform] = {
// If outputForm is equal-to or higher than inputForm, nothing to lower
if (outputForm >= inputForm) {
Seq.empty
} else {
inputForm match {
case ChirrtlForm =>
Seq(new ChirrtlToHighFirrtl) ++ getLoweringTransforms(HighForm, outputForm)
case HighForm =>
Seq(new IRToWorkingIR, new ResolveAndCheck, new transforms.DedupModules, new HighFirrtlToMiddleFirrtl) ++
getLoweringTransforms(MidForm, outputForm)
case MidForm => Seq(new MiddleFirrtlToLowFirrtl) ++ getLoweringTransforms(LowForm, outputForm)
case LowForm => throwInternalError("getLoweringTransforms - LowForm") // should be caught by if above
case UnknownForm => throwInternalError("getLoweringTransforms - UnknownForm") // should be caught by if above
}
}
}
/** Merge a Seq of lowering transforms with custom transforms
*
* Custom Transforms are inserted based on their [[Transform.inputForm]] and [[Transform.outputForm]] with any
* [[Emitter]]s being scheduled last. Custom transforms are inserted in order at the last location in the Seq of
* transforms where previous.outputForm == customTransform.inputForm. If a customTransform outputs a higher form than
* input, [[getLoweringTransforms]] is used to relower the circuit.
*
* @example
* {{{
* // Let Transforms be represented by CircuitForm => CircuitForm
* val A = HighForm => MidForm
* val B = MidForm => LowForm
* val lowering = List(A, B) // Assume these transforms are used by getLoweringTransforms
* // Some custom transforms
* val C = LowForm => LowForm
* val D = MidForm => MidForm
* val E = LowForm => HighForm
* // All of the following comparisons are true
* mergeTransforms(lowering, List(C)) == List(A, B, C)
* mergeTransforms(lowering, List(D)) == List(A, D, B)
* mergeTransforms(lowering, List(E)) == List(A, B, E, A, B)
* mergeTransforms(lowering, List(C, E)) == List(A, B, C, E, A, B)
* mergeTransforms(lowering, List(E, C)) == List(A, B, E, A, B, C)
* // Notice that in the following, custom transform order is NOT preserved (see note)
* mergeTransforms(lowering, List(C, D)) == List(A, D, B, C)
* }}}
*
* @note Order will be preserved for custom transforms so long as the
* inputForm of a latter transforms is equal to or lower than the outputForm
* of the previous transform.
*/
@deprecated("Use a TransformManager with custom targets. This will be removed in 1.3.", "1.2")
def mergeTransforms(lowering: Seq[Transform], custom: Seq[Transform]): Seq[Transform] = {
custom
.sortWith{
case (a, b) => (a, b) match {
case (_: Emitter, _: Emitter) => false
case (_, _: Emitter) => true
case _ => false }}
.foldLeft(lowering) { case (transforms, xform) =>
val index = transforms lastIndexWhere (_.outputForm == xform.inputForm)
assert(index >= 0 || xform.inputForm == ChirrtlForm, // If ChirrtlForm just put at front
s"No transform in $lowering has outputForm ${xform.inputForm} as required by $xform")
val (front, back) = transforms.splitAt(index + 1) // +1 because we want to be AFTER index
front ++ List(xform) ++ getLoweringTransforms(xform.outputForm, xform.inputForm) ++ back
}
}
}
@deprecated("Use a TransformManager requesting which transforms you want to run. This will be removed in 1.3.", "1.2")
trait Compiler extends LazyLogging {
def emitter: Emitter
/** The sequence of transforms this compiler will execute
* @note The inputForm of a given transform must be higher than or equal to the ouputForm of the
* preceding transform. See [[CircuitForm]]
*/
def transforms: Seq[Transform]
require(transforms.size >= 1,
s"Compiler transforms for '${this.getClass.getName}' must have at least ONE Transform! " +
"Use IdentityTransform if you need an identity/no-op transform.")
// Similar to (input|output)Form on [[Transform]] but derived from this Compiler's transforms
def inputForm: CircuitForm = transforms.head.inputForm
def outputForm: CircuitForm = transforms.last.outputForm
private def transformsLegal(xforms: Seq[Transform]): Boolean =
if (xforms.size < 2) {
true
} else {
xforms.sliding(2, 1)
.map { case Seq(p, n) => n.inputForm >= p.outputForm }
.reduce(_ && _)
}
assert(transformsLegal(transforms),
"Illegal Compiler, each transform must be able to accept the output of the previous transform!")
/** Perform compilation
*
* @param state The Firrtl AST to compile
* @param writer The java.io.Writer where the output of compilation will be emitted
* @param customTransforms Any custom [[Transform]]s that will be inserted
* into the compilation process by [[CompilerUtils.mergeTransforms]]
*/
@deprecated("Please use compileAndEmit or other compile method instead", "firrtl 1.0")
def compile(state: CircuitState,
writer: Writer,
customTransforms: Seq[Transform] = Seq.empty): CircuitState = {
val finalState = compileAndEmit(state, customTransforms)
writer.write(finalState.getEmittedCircuit.value)
finalState
}
/** Perform compilation and emit the whole Circuit
*
* This is intended as a convenience method wrapping up Annotation creation for the common case.
* It creates a [[EmitCircuitAnnotation]] that will be consumed by this Transform's emitter. The
* [[EmittedCircuit]] can be extracted from the returned [[CircuitState]] via
* [[CircuitState.emittedCircuitOption]]
*
* @param state The Firrtl AST to compile
* @param customTransforms Any custom [[Transform]]s that will be inserted
* into the compilation process by [[CompilerUtils.mergeTransforms]]
* @return result of compilation with emitted circuit annotated
*/
def compileAndEmit(state: CircuitState,
customTransforms: Seq[Transform] = Seq.empty): CircuitState = {
val emitAnno = EmitCircuitAnnotation(emitter.getClass)
compile(state.copy(annotations = emitAnno +: state.annotations), emitter +: customTransforms)
}
/** Perform compilation
*
* Emission will only be performed if [[EmitAnnotation]]s are present
*
* @param state The Firrtl AST to compile
* @param customTransforms Any custom [[Transform]]s that will be inserted into the compilation
* process by [[CompilerUtils.mergeTransforms]]
* @return result of compilation
*/
def compile(state: CircuitState, customTransforms: Seq[Transform]): CircuitState = {
val allTransforms = CompilerUtils.mergeTransforms(transforms, customTransforms)
val (timeMillis, finalState) = Utils.time {
allTransforms.foldLeft(state) { (in, xform) =>
try {
xform.runTransform(in)
} catch {
// Wrap exceptions from custom transforms so they are reported as such
case e: Exception if CatchCustomTransformExceptions.isCustomTransform(xform) =>
throw CustomTransformException(e)
}
}
}
logger.warn(f"Total FIRRTL Compile Time: $timeMillis%.1f ms")
finalState
}
}
|