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
|
package firrtl
import firrtl.ir._
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 {
/** Returns true if a valid Module name */
val SerializedModuleName = """([a-zA-Z_][a-zA-Z_0-9~!@#$%^*\-+=?/]*)""".r
def validModuleName(s: String): Boolean = s match {
case SerializedModuleName(name) => true
case _ => false
}
/** Returns true if a valid component/subcomponent name */
val SerializedComponentName = """([a-zA-Z_][a-zA-Z_0-9\[\]\.~!@#$%^*\-+=?/]*)""".r
def validComponentName(s: String): Boolean = s match {
case SerializedComponentName(name) => true
case _ => false
}
/** Tokenizes a string with '[', ']', '.' as tokens, e.g.:
* "foo.bar[boo.far]" becomes Seq("foo" "." "bar" "[" "boo" "." "far" "]")
*/
def tokenize(s: String): Seq[String] = s.find(c => "[].".contains(c)) match {
case Some(_) =>
val i = s.indexWhere(c => "[].".contains(c))
Seq(s.slice(0, i), s(i).toString) ++ tokenize(s.drop(i + 1))
case None => Seq(s)
}
/** Given a serialized component/subcomponent reference, subindex, subaccess,
* or subfield, return the corresponding IR expression.
*/
def toExp(s: String): Expression = {
def parse(tokens: Seq[String]): Expression = {
val DecPattern = """([1-9]\d*)""".r
def findClose(tokens: Seq[String], index: Int, nOpen: Int): Seq[String] =
if(index >= tokens.size) {
error("Cannot find closing bracket ]")
} else tokens(index) match {
case "[" => findClose(tokens, index + 1, nOpen + 1)
case "]" if nOpen == 1 => tokens.slice(1, index)
case _ => findClose(tokens, index + 1, nOpen)
}
def buildup(e: Expression, tokens: Seq[String]): Expression = tokens match {
case "[" :: tail =>
val indexOrAccess = findClose(tokens, 0, 0)
indexOrAccess.head match {
case DecPattern(d) => SubIndex(e, d.toInt, UnknownType)
case _ => buildup(SubAccess(e, parse(indexOrAccess), UnknownType), tokens.slice(1, indexOrAccess.size))
}
case "." :: tail =>
buildup(SubField(e, tokens(1), UnknownType), tokens.drop(2))
case Nil => e
}
val root = Reference(tokens.head, UnknownType)
buildup(root, tokens.tail)
}
if(validComponentName(s)) {
parse(tokenize(s))
} else error(s"Cannot convert $s into an expression.")
}
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 {
if(!validModuleName(name)) throw AnnotationException(s"Illegal circuit name: $name")
}
case class ModuleName(name: String, circuit: CircuitName) extends Named {
if(!validModuleName(name)) throw AnnotationException(s"Illegal module name: $name")
}
case class ComponentName(name: String, module: ModuleName) extends Named {
if(!validComponentName(name)) throw AnnotationException(s"Illegal component name: $name")
def expr: Expression = toExp(name)
}
/**
* 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: (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)
}
}
|