-
Notifications
You must be signed in to change notification settings - Fork 269
Expand file tree
/
Copy pathannotations.scala
More file actions
546 lines (492 loc) · 19.9 KB
/
Copy pathannotations.scala
File metadata and controls
546 lines (492 loc) · 19.9 KB
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
// See LICENSE for license details.
package midas.targetutils
import chisel3.{
dontTouch,
fromBooleanToLiteral,
when,
Bits,
Bool,
Clock,
Data,
MemBase,
Module,
Printable,
RegNext,
Reset,
UInt,
Wire,
WireDefault,
}
import chisel3.printf.Printf
import chisel3.experimental.{annotate, requireIsHardware, BaseModule, ChiselAnnotation}
import firrtl.RenameMap
import firrtl.annotations.{
Annotation,
ComponentName,
HasSerializationHints,
InstanceTarget,
ModuleTarget,
ReferenceTarget,
SingleTargetAnnotation,
}
/** These are consumed by [[midas.passes.AutoILATransform]] to directly instantiate an ILA at the top of simulator's
* design hierarchy (the PlatformShim level).
*/
case class FpgaDebugAnnotation(target: Data) extends ChiselAnnotation {
def toFirrtl = FirrtlFpgaDebugAnnotation(target.toNamed)
}
case class FirrtlFpgaDebugAnnotation(target: ComponentName) extends SingleTargetAnnotation[ComponentName] {
def duplicate(n: ComponentName) = this.copy(target = n)
}
object FpgaDebug {
def apply(targets: Data*): Unit = {
targets.foreach { requireIsHardware(_, "Target passed to FpgaDebug:") }
targets.map({ t => annotate(FpgaDebugAnnotation(t)) })
}
}
private[midas] class ReferenceTargetRenamer(renames: RenameMap) {
// TODO: determine order for multiple renames, or just check of == 1 rename?
def exactRename(rt: ReferenceTarget): ReferenceTarget = {
val renameMatches = renames.get(rt).getOrElse(Seq(rt)).collect({ case rt: ReferenceTarget => rt })
assert(
renameMatches.length <= 1,
s"${rt} should be renamed exactly once (or not at all). Suggested renames: ${renameMatches}",
)
renameMatches.headOption.getOrElse(rt)
}
def apply(rt: ReferenceTarget): Seq[ReferenceTarget] = {
renames.get(rt).getOrElse(Seq(rt)).collect({ case rt: ReferenceTarget => rt })
}
}
private[midas] case class SynthPrintfAnnotation(
target: ReferenceTarget
) extends firrtl.annotations.SingleTargetAnnotation[ReferenceTarget] {
def duplicate(newTarget: ReferenceTarget) = this.copy(newTarget)
}
object SynthesizePrintf {
/** Annotates a chisel printf as a candidate for synthesis. The printf is only synthesized if Printf synthesis is
* enabled in Golden Gate.
*
* See: https://docs.fires.im/en/stable/search.html?q=Printf+Synthesis&check_keywords=yes&area=default
*
* @param printf
* The printf statement to be synthesized.
*
* @return
* The original input, so that this annotator may be applied inline if desired.
*/
def apply(printf: Printf): Printf = {
annotate(new ChiselAnnotation {
def toFirrtl = SynthPrintfAnnotation(printf.toTarget)
})
printf
}
private def generateAnnotations(format: String, args: Seq[Bits], name: Option[String]): Printable = {
Module.currentModule.getOrElse(throw new RuntimeException("Cannot annotate a printf outside of a Module"))
// To preserve the behavior of the printf parameter annotator, generate a
// secondary printf and annotate that, instead of the user's printf, which
// will be given an empty string. This will be removed with the apply methods in 1.15.
val printf = SynthesizePrintf(chisel3.printf(Printable.pack(format, args: _*)))
name.foreach { n => printf.suggestName(n) }
Printable.pack("")
}
/** Annotates* a printf by intercepting the parameters to a chisel printf, and returning a printable. As a side
* effect, this function generates a ChiselSynthPrintfAnnotation with the format string and references to each of the
* args.
*
* *Note: this isn't actually annotating the statement but instead the arguments. This is a vestige from earlier
* versions of chisel / firrtl in which print statements were unnamed, and thus not referenceable from annotations.
*
* @param format
* The format string for the printf
* @param args
* Hardware references to populate the format string.
*/
@deprecated("This method will be removed. Annotate the printf statement directly", "FireSim 1.14")
def apply(format: String, args: Bits*): Printable = generateAnnotations(format, args, None)
/** Like the other apply method, but provides an optional name which can be used by synthesized hardware / bridge.
* Generally, users deploy the nameless form.
*
* @param name
* A descriptive name for this printf instance.
* @param format
* The format string for the printf
* @param args
* Hardware references to populate the format string.
*/
@deprecated("This method will be removed. Annotate the printf statement directly", "FireSim 1.14")
def apply(name: String, format: String, args: Bits*): Printable =
generateAnnotations(format, args, Some(name))
}
/** A mixed-in ancestor trait for all FAME annotations, useful for type-casing.
*/
trait FAMEAnnotation {
this: Annotation =>
}
/** This labels an instance so that it is extracted as a separate FAME model.
*/
case class FAMEModelAnnotation(target: BaseModule) extends ChiselAnnotation {
def toFirrtl: FirrtlFAMEModelAnnotation = {
val parent = ModuleTarget(target.toNamed.circuit.name, target.parentModName)
FirrtlFAMEModelAnnotation(parent.instOf(target.instanceName, target.name))
}
}
case class FirrtlFAMEModelAnnotation(
target: InstanceTarget
) extends SingleTargetAnnotation[InstanceTarget]
with FAMEAnnotation {
def targets = Seq(target)
def duplicate(n: InstanceTarget) = this.copy(n)
}
/** This specifies that the module should be automatically multi-threaded (Chisel annotator).
*/
case class EnableModelMultiThreadingAnnotation(target: BaseModule) extends ChiselAnnotation {
def toFirrtl: FirrtlEnableModelMultiThreadingAnnotation = {
val parent = ModuleTarget(target.toNamed.circuit.name, target.parentModName)
FirrtlEnableModelMultiThreadingAnnotation(parent.instOf(target.instanceName, target.name))
}
}
/** This specifies that the module should be automatically multi-threaded (FIRRTL annotation).
*/
case class FirrtlEnableModelMultiThreadingAnnotation(
target: InstanceTarget
) extends SingleTargetAnnotation[InstanceTarget]
with FAMEAnnotation {
def targets = Seq(target)
def duplicate(n: InstanceTarget) = this.copy(n)
}
/** This labels a target Mem so that it is extracted and replaced with a separate model.
*/
case class MemModelAnnotation[T <: Data](target: MemBase[T]) extends ChiselAnnotation {
def toFirrtl = FirrtlMemModelAnnotation(target.toNamed.toTarget)
}
case class FirrtlMemModelAnnotation(target: ReferenceTarget) extends SingleTargetAnnotation[ReferenceTarget] {
def duplicate(rt: ReferenceTarget) = this.copy(target = rt)
}
case class ExcludeInstanceAssertsAnnotation(target: (String, String)) extends firrtl.annotations.NoTargetAnnotation {
def duplicate(n: (String, String)) = this.copy(target = n)
}
// TODO: Actually use a real target and not strings.
object ExcludeInstanceAsserts {
def apply(target: (String, String)): ChiselAnnotation =
new ChiselAnnotation {
def toFirrtl = ExcludeInstanceAssertsAnnotation(target)
}
}
sealed trait PerfCounterOpType
object PerfCounterOps {
/** Takes the annotated UInt and adds it to an accumulation register generated in the bridge
*/
case object Accumulate extends PerfCounterOpType
/** Takes the annotated UInt and exposes it directly to the driver NB: Fields longer than 64b are not supported, and
* must be divided into smaller segments that are sepearate annotated
*/
case object Identity extends PerfCounterOpType
}
/** AutoCounter annotations. Do not emit the FIRRTL annotations unless you are writing a target transformation, use the
* Chisel-side [[PerfCounter]] object instead.
*/
case class AutoCounterFirrtlAnnotation(
target: ReferenceTarget,
clock: ReferenceTarget,
reset: ReferenceTarget,
label: String,
description: String,
opType: PerfCounterOpType = PerfCounterOps.Accumulate,
coverGenerated: Boolean = false,
) extends firrtl.annotations.Annotation
with HasSerializationHints {
def update(renames: RenameMap): Seq[firrtl.annotations.Annotation] = {
val renamer = new ReferenceTargetRenamer(renames)
val renamedTarget = renamer.exactRename(target)
val renamedClock = renamer.exactRename(clock)
val renamedReset = renamer.exactRename(reset)
Seq(this.copy(target = renamedTarget, clock = renamedClock, reset = renamedReset))
}
// The AutoCounter tranform will reject this annotation if it's not enclosed
def shouldBeIncluded(modList: Seq[String]): Boolean = !coverGenerated || modList.contains(target.module)
def enclosingModule(): String = target.module
def enclosingModuleTarget(): ModuleTarget = ModuleTarget(target.circuit, enclosingModule())
def typeHints: Seq[Class[_]] = Seq(opType.getClass)
}
case class AutoCounterCoverModuleFirrtlAnnotation(target: ModuleTarget)
extends SingleTargetAnnotation[ModuleTarget]
with FAMEAnnotation {
def duplicate(n: ModuleTarget) = this.copy(target = n)
}
case class AutoCounterCoverModuleAnnotation(target: ModuleTarget) extends ChiselAnnotation {
def toFirrtl = AutoCounterCoverModuleFirrtlAnnotation(target)
}
object PerfCounter {
private def emitAnnotation(
target: UInt,
clock: Clock,
reset: Reset,
label: String,
description: String,
opType: PerfCounterOpType,
): Unit = {
requireIsHardware(target, "Target passed to PerfCounter:")
requireIsHardware(clock, "Clock passed to PerfCounter:")
requireIsHardware(reset, "Reset passed to PerfCounter:")
annotate(new ChiselAnnotation {
def toFirrtl =
AutoCounterFirrtlAnnotation(target.toTarget, clock.toTarget, reset.toTarget, label, description, opType)
})
}
/** Labels a signal as an event for which an host-side counter (an "AutoCounter") should be generated). Events can be
* multi-bit to encode multiple occurances in a cycle (e.g., the number of instructions retired in a superscalar
* processor). NB: Golden Gate will not generate the coutner unless AutoCounter is enabled in your the platform
* config. See the docs.fires.im for end-to-end usage information.
*
* @param target
* The number of occurances of the event (in the current cycle)
*
* @param clock
* The clock to which this event is sychronized.
*
* @param reset
* If the event is asserted while under the provide reset, it is not counted. TODO: This should be made optional.
*
* @param label
* A verilog-friendly identifier for the event signal
*
* @param description
* A human-friendly description of the event.
*
* @param opType
* Defines how the bridge should be aggregated into a performance counter.
*/
def apply(
target: UInt,
clock: Clock,
reset: Reset,
label: String,
description: String,
opType: PerfCounterOpType = PerfCounterOps.Accumulate,
): Unit =
emitAnnotation(target, clock, reset, label, description, opType)
/** A simplified variation of the full apply method above that uses the implicit clock and reset.
*/
def apply(target: UInt, label: String, description: String): Unit =
emitAnnotation(target, Module.clock, Module.reset, label, description, PerfCounterOps.Accumulate)
/** Passes the annotated UInt through to the driver without accumulation. Use cases:
* - Custom accumulation / counting logic not supported by the driver
* - Providing runtime metadata along side standard accumulation registers
*
* Note: Under reset, the passthrough value is set to 0. This keeps event handling uniform in the transform.
*/
def identity(target: UInt, label: String, description: String): Unit = {
require(
target.getWidth <= 64,
s"""|PerfCounter.identity can only accept fields <= 64b wide. Provided target for label:
| $label
|was ${target.getWidth}b.""".stripMargin,
)
emitAnnotation(target, Module.clock, Module.reset, label, description, opType = PerfCounterOps.Identity)
}
}
case class PlusArgFirrtlAnnotation(
target: InstanceTarget
) extends SingleTargetAnnotation[InstanceTarget]
with FAMEAnnotation {
def targets = Seq(target)
def duplicate(n: InstanceTarget) = this.copy(n)
}
object PlusArg {
private def emitAnnotation(
target: BaseModule
): Unit = {
annotate(new ChiselAnnotation {
def toFirrtl = {
val parent = ModuleTarget(target.toNamed.circuit.name, target.parentModName)
PlusArgFirrtlAnnotation(parent.instOf(target.instanceName, target.name))
}
})
}
/** Labels a Rocket Chip 'plusarg_reader' module to synthesize. Must be of the type found in
* https://github.com/chipsalliance/rocket-chip/blob/master/src/main/scala/util/PlusArg.scala
*
* @param target
* The 'plusarg_reader' module to synthesize
*/
def apply(target: BaseModule): Unit = {
emitAnnotation(target)
}
}
// Need serialization utils to be upstreamed to FIRRTL before i can use these.
//sealed trait TriggerSourceType
//case object Credit extends TriggerSourceType
//case object Debit extends TriggerSourceType
case class TriggerSourceAnnotation(
target: ReferenceTarget,
clock: ReferenceTarget,
reset: Option[ReferenceTarget],
sourceType: Boolean,
) extends Annotation
with FAMEAnnotation {
def update(renames: RenameMap): Seq[firrtl.annotations.Annotation] = {
val renamer = new ReferenceTargetRenamer(renames)
val renamedTarget = renamer.exactRename(target)
val renamedClock = renamer.exactRename(clock)
val renamedReset = reset.map(renamer.exactRename)
Seq(this.copy(target = renamedTarget, clock = renamedClock, reset = renamedReset))
}
def enclosingModuleTarget(): ModuleTarget = ModuleTarget(target.circuit, target.module)
def enclosingModule(): String = target.module
}
case class TriggerSinkAnnotation(
target: ReferenceTarget,
clock: ReferenceTarget,
) extends Annotation
with FAMEAnnotation {
def update(renames: RenameMap): Seq[firrtl.annotations.Annotation] = {
val renamer = new ReferenceTargetRenamer(renames)
val renamedTarget = renamer.exactRename(target)
val renamedClock = renamer.exactRename(clock)
Seq(this.copy(target = renamedTarget, clock = renamedClock))
}
def enclosingModuleTarget(): ModuleTarget = ModuleTarget(target.circuit, target.module)
}
object TriggerSource {
private def annotateTrigger(tpe: Boolean)(target: Bool, reset: Option[Bool]): Unit = {
// Hack: Create dummy nodes until chisel-side instance annotations have been improved
val clock = WireDefault(Module.clock)
reset.map(dontTouch.apply)
requireIsHardware(target, "Target passed to TriggerSource:")
reset.foreach { requireIsHardware(_, "Reset passed to TriggerSource:") }
annotate(new ChiselAnnotation {
def toFirrtl =
TriggerSourceAnnotation(target.toNamed.toTarget, clock.toNamed.toTarget, reset.map(_.toTarget), tpe)
})
}
def annotateCredit = annotateTrigger(true) _
def annotateDebit = annotateTrigger(false) _
/** Methods to annotate a Boolean as a trigger credit or debit. Credits and debits issued while the module's implicit
* reset is asserted are not counted.
*/
def credit(credit: Bool): Unit = annotateCredit(credit, Some(Module.reset.asBool))
def debit(debit: Bool): Unit = annotateDebit(debit, Some(Module.reset.asBool))
def apply(creditSig: Bool, debitSig: Bool): Unit = {
credit(creditSig)
debit(debitSig)
}
/** Variations of the above methods that count credits and debits provided while the implicit reset is asserted.
*/
def creditEvenUnderReset(credit: Bool): Unit = annotateCredit(credit, None)
def debitEvenUnderReset(debit: Bool): Unit = annotateDebit(debit, None)
def evenUnderReset(creditSig: Bool, debitSig: Bool): Unit = {
creditEvenUnderReset(creditSig)
debitEvenUnderReset(debitSig)
}
/** Level sensitive trigger sources. Implemented using [[credit]] and [[debit]]. Note: This generated hardware in your
* target design.
*
* @param src
* Enables the trigger when asserted. If no other credits have been issued since (e.g., a second level-sensitive
* enable was asserted), the trigger is disabled when src is desasserted.
*/
def levelSensitiveEnable(src: Bool): Unit = {
val srcLast = RegNext(src)
credit(src && !srcLast)
debit(!src && srcLast)
}
}
object TriggerSink {
/** Marks a bool as receiving the global trigger signal.
*
* @param target
* A Bool node that will be driven with the trigger
*
* @param noSourceDefault
* The value that the trigger signal should take on if no trigger soruces are found in the target. This is a
* temporary parameter required while this apply method generates a wire. Otherwise this can be punted to the
* target's RTL.
*/
def apply(target: Bool, noSourceDefault: => Bool = true.B): Unit = {
// Hack: Create dummy nodes until chisel-side instance annotations have been improved
val targetWire = WireDefault(noSourceDefault)
val clock = Module.clock
target := targetWire
// Both the provided node and the generated one need to be dontTouched to stop
// constProp from optimizing the down stream logic(?)
dontTouch(target)
annotate(new ChiselAnnotation {
def toFirrtl = TriggerSinkAnnotation(targetWire.toTarget, clock.toTarget)
})
}
/** Syntatic sugar for a when context that is predicated by a trigger sink. Example usage:
* {{{
* TriggerSink.whenEnabled {
* printf(<...>)
* }
* }}}
*
* @param noSourceDefault
* See [[TriggerSink.apply]].
*/
def whenEnabled(noSourceDefault: => Bool = true.B)(elaborator: => Unit): Unit = {
val sinkEnable = Wire(Bool())
apply(sinkEnable, noSourceDefault)
when(sinkEnable) { elaborator }
}
}
case class RoCCBusyFirrtlAnnotation(
target: ReferenceTarget,
ready: ReferenceTarget,
valid: ReferenceTarget,
) extends firrtl.annotations.Annotation
with FAMEAnnotation {
def update(renames: RenameMap): Seq[firrtl.annotations.Annotation] = {
val renamer = new ReferenceTargetRenamer(renames)
val renamedReady = renamer.exactRename(ready)
val renamedValid = renamer.exactRename(valid)
val renamedTarget = renamer.exactRename(target)
Seq(this.copy(target = renamedTarget, ready = renamedReady, valid = renamedValid))
}
def enclosingModuleTarget(): ModuleTarget = ModuleTarget(target.circuit, target.module)
def enclosingModule(): String = target.module
}
object MakeRoCCBusyLatencyInsensitive {
def apply(
target: Bool,
ready: Bool,
valid: Bool,
): Unit = {
requireIsHardware(target, "Target passed to ..:")
requireIsHardware(ready, "Ready passed to ..:")
requireIsHardware(valid, "Valid passed to ..:")
annotate(new ChiselAnnotation {
def toFirrtl = RoCCBusyFirrtlAnnotation(target.toNamed.toTarget, ready.toNamed.toTarget, valid.toNamed.toTarget)
})
}
}
case class FirrtlPartWrapperParentAnnotation(
target: InstanceTarget
) extends SingleTargetAnnotation[InstanceTarget]
with FAMEAnnotation {
def targets = Seq(target)
def duplicate(n: InstanceTarget) = this.copy(n)
}
case class FirrtlPortToNeighborRouterIdxAnno(
target: ReferenceTarget,
extractNeighborIdx: Int,
removeNeighborIdx: Int,
) extends firrtl.annotations.Annotation
with FAMEAnnotation {
def update(renames: RenameMap): Seq[firrtl.annotations.Annotation] = {
val renamer = new ReferenceTargetRenamer(renames)
val renameTarget = renamer.exactRename(target)
Seq(this.copy(target = renameTarget))
}
}
case class FirrtlCombLogicInsideModuleAnno(
target: ReferenceTarget
) extends firrtl.annotations.Annotation
with FAMEAnnotation {
def update(renames: RenameMap): Seq[firrtl.annotations.Annotation] = {
val renamer = new ReferenceTargetRenamer(renames)
val renameTarget = renamer.exactRename(target)
Seq(this.copy(target = renameTarget))
}
}