aboutsummaryrefslogtreecommitdiff
path: root/src/main/scala/firrtl/annotations/Annotation.scala
blob: 16f85e67227699064e8ad29d79a59bcc2c8086a7 (plain)
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
// See LICENSE for license details.

package firrtl
package annotations

import firrtl.options.StageUtils

case class AnnotationException(message: String) extends Exception(message)

/** Base type of auxiliary information */
trait Annotation extends Product {

  /** Update the target based on how signals are renamed */
  def update(renames: RenameMap): Seq[Annotation]

  /** Optional pretty print
    *
    * @note rarely used
    */
  def serialize: String = this.toString

  /** Recurses through ls to find all [[Target]] instances
    * @param ls
    * @return
    */
  private def extractComponents(ls: scala.collection.Traversable[_]): Seq[Target] = {
    ls.collect {
      case c: Target                          => Seq(c)
      case o: Product                         => extractComponents(o.productIterator.toIterable)
      case x: scala.collection.Traversable[_] => extractComponents(x)
    }.foldRight(Seq.empty[Target])((seq, c) => c ++ seq)
  }

  /** Returns all [[firrtl.annotations.Target Target]] members in this annotation
    * @return
    */
  def getTargets: Seq[Target] = extractComponents(productIterator.toSeq)
}

/** If an Annotation does not target any [[Named]] thing in the circuit, then all updates just
  * return the Annotation itself
  */
trait NoTargetAnnotation extends Annotation {
  def update(renames: RenameMap): Seq[NoTargetAnnotation] = Seq(this)
}

/** An Annotation that targets a single [[Named]] thing */
trait SingleTargetAnnotation[T <: Named] extends Annotation {
  val target: T

  /** Create another instance of this Annotation */
  def duplicate(n: T): Annotation

  // This mess of @unchecked and try-catch is working around the fact that T is unknown due to type
  // erasure. We cannot that newTarget is of type T, but a CastClassException will be thrown upon
  // invoking duplicate if newTarget cannot be cast to T (only possible in the concrete subclass)
  def update(renames: RenameMap): Seq[Annotation] = {
    target match {
      case c: Target =>
        val x = renames.get(c)
        x.map(newTargets => newTargets.map(t => duplicate(t.asInstanceOf[T]))).getOrElse(List(this))
      case from: Named =>
        val ret = renames.get(Target.convertNamed2Target(target))
        ret
          .map(_.map { newT =>
            val result = newT match {
              case c: InstanceTarget => ModuleName(c.ofModule, CircuitName(c.circuit))
              case c: IsMember =>
                val local = Target.referringModule(c)
                c.setPathTarget(local)
              case c: CircuitTarget => c.toNamed
              case other => throw Target.NamedException(s"Cannot convert $other to [[Named]]")
            }
            Target.convertTarget2Named(result) match {
              case newTarget: T @unchecked =>
                try {
                  duplicate(newTarget)
                } catch {
                  case _: java.lang.ClassCastException =>
                    val msg = s"${this.getClass.getName} target ${target.getClass.getName} " +
                      s"cannot be renamed to ${newTarget.getClass}"
                    throw AnnotationException(msg)
                }
            }
          })
          .getOrElse(List(this))
    }
  }
}

/** [[MultiTargetAnnotation]] keeps the renamed targets grouped within a single annotation. */
trait MultiTargetAnnotation extends Annotation {

  /** Contains a sequence of [[firrtl.annotations.Target Target]].
    * When created, [[targets]] should be assigned by `Seq(Seq(TargetA), Seq(TargetB), Seq(TargetC))`
    */
  val targets: Seq[Seq[Target]]

  /** Create another instance of this Annotation */
  def duplicate(n: Seq[Seq[Target]]): Annotation

  /** Assume [[RenameMap]] is `Map(TargetA -> Seq(TargetA1, TargetA2, TargetA3), TargetB -> Seq(TargetB1, TargetB2))`
    * in the update, this Annotation is still one annotation, but the contents are renamed in the below form
    * Seq(Seq(TargetA1, TargetA2, TargetA3), Seq(TargetB1, TargetB2), Seq(TargetC))
    */
  def update(renames: RenameMap): Seq[Annotation] = Seq(duplicate(targets.map(ts => ts.flatMap(renames(_)))))

  private def crossJoin[T](list: Seq[Seq[T]]): Seq[Seq[T]] =
    list match {
      case Nil      => Nil
      case x :: Nil => x.map(Seq(_))
      case x :: xs =>
        val xsJoin = crossJoin(xs)
        for {
          i <- x
          j <- xsJoin
        } yield {
          Seq(i) ++ j
        }
    }

  /** Assume [[RenameMap]] is `Map(TargetA -> Seq(TargetA1, TargetA2, TargetA3), TargetB -> Seq(TargetB1, TargetB2))`
    * After flat, this Annotation will be flat to the [[AnnotationSeq]] in the below form
    * Seq(Seq(TargetA1), Seq(TargetB1), Seq(TargetC)); Seq(Seq(TargetA1), Seq(TargetB2), Seq(TargetC))
    * Seq(Seq(TargetA2), Seq(TargetB1), Seq(TargetC)); Seq(Seq(TargetA2), Seq(TargetB2), Seq(TargetC))
    * Seq(Seq(TargetA3), Seq(TargetB1), Seq(TargetC)); Seq(Seq(TargetA3), Seq(TargetB2), Seq(TargetC))
    */
  def flat(): AnnotationSeq = crossJoin(targets).map(r => duplicate(r.map(Seq(_))))
}

object Annotation

case class DeletedAnnotation(xFormName: String, anno: Annotation) extends NoTargetAnnotation {
  override def serialize: String = s"""DELETED by $xFormName\n${anno.serialize}"""
}