I'd like to provide a more complete example, but I wanted to make sure to at least write something up. This also needs to be fleshed out as a proper example/tutorial on the website.
FIRRTL has robust support for tracking names of signals across built-in and custom transformations. This is a case where the infrastructure is all there, but it's very much a power user API. In short, you can create FIRRTL Annotations that will track Targets. You can then emit custom metadata files or use the normal FIRRTL annotation file (try the CLI option -foaf
/ --output-annotation-file
).
An example FIRRTL Annotation that has will emit a custom metadata file at the end of compilation:
// Example FIRRTL annotation with Custom serialization
// FIRRTL will track the name of this signal through compilation
case class MyMetadataAnno(target: ReferenceTarget)
extends SingleTargetAnnotation[ReferenceTarget]
with CustomFileEmission {
def duplicate(n: ReferenceTarget) = this.copy(n)
// API for serializing a custom metadata file
// Note that multiple of this annotation will collide which is an error, not handled in this example
protected def baseFileName(annotations: AnnotationSeq): String = "my_metadata"
protected def suffix: Option[String] = Some(".txt")
def getBytes: Iterable[Byte] =
s"Annotated signal: ${target.serialize}".getBytes
}
The case class
declaration and duplicate
method are enough to track a single signal through compilation. The CustomFileEmission
and related baseFileName
, suffix
, and getBytes
methods define how to serialize my custom metadata file. As mentioned in the comment, as implemented in this example we can only have 1 instance of this MyMetadataAnno
or they will try to write the same file which is an error. This can be handled by customizing the filename based on the Target
, or writing a FIRRTL transform to aggregate multiple of this annotation into a single annotation.
We then need a way to create this annotation in Chisel:
def markSignal[T <: Data](x: T): T = {
annotate(new ChiselAnnotation {
// We can't call .toTarget until end of Chisel elaboration
// .toFirrtl is called by Chisel at the end of elaboration
def toFirrtl = MyMetadataAnno(x.toTarget)
})
x
}
Now all we need to do is use this simple API in our Chisel
// Simple example with a marked signal
class Foo extends MultiIOModule {
val in = IO(Flipped(Decoupled(UInt(8.W))))
val out = IO(Decoupled(UInt(8.W)))
markSignal(out.valid)
out <> in
}
This will result in writing the file my_metadata.txt
to the target directory with the contents:
Annotated signal: ~Foo|Foo>out_valid
Note that this is special FIRRTL target syntax saying that out_valid
is the annotated signal that lives in module Foo
.
Complete code in an executable example:
https://scastie.scala-lang.org/moEiIqZPTRCR5mLQNrV3zA