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
|
package firrtl
import scala.collection.mutable
import java.io.Writer
/**
* Firrtl Annotation Library
*
* WARNING(izraelevitz): Untested, and requires unit tests, which require the
* LowerTypes pass and ConstProp pass to correctly populate its RenameMap.
*
* The following tables explain how Tenacity and Permissibility interact
* with different RenameMap scenarios:
* x -> x : a component named "x" is renamed to the same name, "x"
* x -> y : a component named "x" is renamed to a different name, "y"
* x -> y,z : a component named "x" is split into two components named "y" and "z"
* x -> () : a component named "x" is removed
*
* Tenacity Propagation Behavior:
* ----------|----------|----------|------------|-----------|
* | x -> x | x -> y | x -> y,z | x -> () |
* ----------|----------|----------|------------|-----------|
* Unstable | x | () | () | () |
* Fickle | x | y | () | () |
* Insistent | x | y | y | () |
* Sticky | x | y | y,z | () |
* ----------|----------|----------|------------|-----------|
*
* Permissibility Accepted Ranges:
* ----------|----------|----------|------------|-----------|
* | x -> x | x -> y | x -> y,z | x -> () |
* ----------|----------|----------|------------|-----------|
* Strict | ok | error | error | error |
* Rigid | ok | ok | error | error |
* Firm | ok | ok | ok | error |
* Loose | ok | ok | ok | ok |
* ----------|----------|----------|------------|-----------|
*/
object Annotations {
case class AnnotationException(message: String) extends Exception(message)
/**
* Named classes associate an annotation with a component in a Firrtl circuit
*/
trait Named { def name: String }
case class CircuitName(name: String) extends Named
case class ModuleName(name: String, circuit: CircuitName) extends Named
case class ComponentName(name: String, module: ModuleName) extends Named
/**
* Transform ID (TransID) associates an annotation with an instantiated
* Firrtl compiler transform
*/
case class TransID(id: Int)
/**
* Permissibility defines the range of acceptable changes to the annotated component.
*/
trait Permissibility {
def check(from: Named, tos: Seq[Named], which: Annotation): Unit
}
/**
* Annotated component cannot be renamed, expanded, or removed.
*/
trait Strict extends Permissibility {
def check(from: Named, tos: Seq[Named], which: Annotation): Unit = tos.size match {
case 0 =>
throw new AnnotationException(s"Cannot remove the strict annotation ${which.serialize} on ${from.name}")
case 1 if(from != tos.head) =>
throw new AnnotationException(s"Cannot rename the strict annotation ${which.serialize} on ${from.name} -> ${tos.head.name}")
case _ =>
throw new AnnotationException(s"Cannot expand a strict annotation on ${from.name} -> ${tos.map(_.name)}")
}
}
/**
* Annotated component can be renamed, but cannot be expanded or removed.
*/
trait Rigid extends Permissibility {
def check(from: Named, tos: Seq[Named], which: Annotation): Unit = tos.size match {
case 0 => throw new AnnotationException(s"Cannot remove the rigid annotation ${which.serialize} on ${from.name}")
case 1 => {}
case _ => throw new AnnotationException(s"Cannot expand a rigid annotation on ${from.name} -> ${tos.map(_.name)}")
}
}
/**
* Annotated component can be renamed, and expanded, but not removed.
*/
trait Firm extends Permissibility {
def check(from: Named, tos: Seq[Named], which: Annotation): Unit = tos.size match {
case 0 => throw new AnnotationException(s"Cannot remove the firm annotation ${which.serialize} on ${from.name}")
case _ => {}
}
}
/**
* Annotated component can be renamed, and expanded, and removed.
*/
trait Loose extends Permissibility {
def check(from: Named, tos: Seq[Named], which: Annotation): Unit = {}
}
/**
* Tenacity defines how the annotation propagates when changes to the
* annotated component occur.
*/
trait Tenacity {
protected def propagate(from: Named, tos: Seq[Named], dup: Named=>Annotation): Seq[Annotation]
}
/**
* Annotation propagates to all new components
*/
trait Sticky extends Tenacity {
protected def propagate(from: Named, tos: Seq[Named], dup: Named=>Annotation): Seq[Annotation] = tos.map(dup(_))
}
/**
* Annotation propagates to the first of all new components
*/
trait Insistent extends Tenacity {
protected def propagate(from: Named, tos: Seq[Named], dup: Named=>Annotation): Seq[Annotation] = tos.headOption match {
case None => Seq.empty
case Some(n) => Seq(dup(n))
}
}
/**
* Annotation propagates only if there is one new component.
*/
trait Fickle extends Tenacity {
protected def propagate(from: Named, tos: Seq[Named], dup: Named=>Annotation): Seq[Annotation] = tos.size match {
case 1 => Seq(dup(tos.head))
case _ => Seq.empty
}
}
/**
* Annotation propagates only the new component shares the same name.
*/
trait Unstable extends Tenacity {
protected def propagate(from: Named, tos: Seq[Named], dup: Named=>Annotation): Seq[Annotation] = tos.size match {
case 1 if(tos.head == from) => Seq(dup(tos.head))
case _ => Seq.empty
}
}
/**
* Annotation associates with a given named circuit component (target) and a
* given transformation (tID). Also defined are the legal ranges of changes
* to the associated component (Permissibility) and how the annotation
* propagates under such changes (Tenacity). Subclasses must implement the
* duplicate function to create the same annotation associated with a new
* component.
*/
trait Annotation extends Permissibility with Tenacity {
def target: Named
def tID: TransID
protected def duplicate(n: Named): Annotation
def serialize: String = this.toString
def update(tos: Seq[Named]): Seq[Annotation] = {
check(target, tos, this)
propagate(target, tos, duplicate _)
}
}
/**
* Container of all annotations for a Firrtl compiler.
*/
case class AnnotationMap(annotations: Seq[Annotation]) {
type NamedMap = Map[Named, Map[TransID, Annotation]]
type IDMap = Map[TransID, Map[Named, Annotation]]
val (namedMap: NamedMap, idMap:IDMap) =
//annotations.foldLeft(Tuple2[NamedMap, IDMap](Map.empty, Map.empty)){
annotations.foldLeft((Map.empty: NamedMap, Map.empty: IDMap)){
(partialMaps: Tuple2[NamedMap, IDMap], annotation: Annotation) => {
val tIDToAnn = partialMaps._1.getOrElse(annotation.target, Map.empty)
val pNMap = partialMaps._1 + (annotation.target -> (tIDToAnn + (annotation.tID -> annotation)))
val nToAnn = partialMaps._2.getOrElse(annotation.tID, Map.empty)
val ptIDMap = partialMaps._2 + (annotation.tID -> (nToAnn + (annotation.target -> annotation)))
Tuple2(pNMap, ptIDMap)
}
}
def get(id: TransID): Option[Map[Named, Annotation]] = idMap.get(id)
def get(named: Named): Option[Map[TransID, Annotation]] = namedMap.get(named)
}
}
|