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
|
// See LICENSE for license details.
package firrtl.transforms
import java.io.{File, FileNotFoundException, FileOutputStream, PrintWriter}
import firrtl._
import firrtl.Utils.throwInternalError
import firrtl.annotations._
import scala.collection.immutable.ListSet
sealed trait BlackBoxHelperAnno extends Annotation
case class BlackBoxTargetDirAnno(targetDir: String) extends BlackBoxHelperAnno
with NoTargetAnnotation {
override def serialize: String = s"targetDir\n$targetDir"
}
case class BlackBoxResourceAnno(target: ModuleName, resourceId: String) extends BlackBoxHelperAnno
with SingleTargetAnnotation[ModuleName] {
def duplicate(n: ModuleName) = this.copy(target = n)
override def serialize: String = s"resource\n$resourceId"
}
case class BlackBoxInlineAnno(target: ModuleName, name: String, text: String) extends BlackBoxHelperAnno
with SingleTargetAnnotation[ModuleName] {
def duplicate(n: ModuleName) = this.copy(target = n)
override def serialize: String = s"inline\n$name\n$text"
}
/** Handle source for Verilog ExtModules (BlackBoxes)
*
* This transform handles the moving of Verilog source for black boxes into the
* target directory so that it can be accessed by verilator or other backend compilers
* While parsing it's annotations it looks for a BlackBoxTargetDir annotation that
* will set the directory where the Verilog will be written. This annotation is typically be
* set by the execution harness, or directly in the tests
*/
class BlackBoxSourceHelper extends firrtl.Transform {
private val DefaultTargetDir = new File(".")
override def inputForm: CircuitForm = LowForm
override def outputForm: CircuitForm = LowForm
/** Collect BlackBoxHelperAnnos and and find the target dir if specified
* @param annos a list of generic annotations for this transform
* @return BlackBoxHelperAnnos and target directory
*/
def collectAnnos(annos: Seq[Annotation]): (ListSet[BlackBoxHelperAnno], File) =
annos.foldLeft((ListSet.empty[BlackBoxHelperAnno], DefaultTargetDir)) {
case ((acc, tdir), anno) => anno match {
case BlackBoxTargetDirAnno(dir) =>
val targetDir = new File(dir)
if (!targetDir.exists()) { FileUtils.makeDirectory(targetDir.getAbsolutePath) }
(acc, targetDir)
case a: BlackBoxHelperAnno => (acc + a, tdir)
case _ => (acc, tdir)
}
}
/**
* write the verilog source for each annotation to the target directory
* @note the state is not changed by this transform
* @param state Input Firrtl AST
* @return A transformed Firrtl AST
*/
override def execute(state: CircuitState): CircuitState = {
val (annos, targetDir) = collectAnnos(state.annotations)
val resourceFiles: ListSet[File] = annos.collect {
case BlackBoxResourceAnno(_, resourceId) =>
val name = resourceId.split("/").last
val outFile = new File(targetDir, name)
(resourceId, outFile)
}.map { case (res, file) =>
BlackBoxSourceHelper.copyResourceToFile(res, file)
file
}
val inlineFiles: ListSet[File] = annos.collect {
case BlackBoxInlineAnno(_, name, text) =>
val outFile = new File(targetDir, name)
(text, outFile)
}.map { case (text, file) =>
BlackBoxSourceHelper.writeTextToFile(text, file)
file
}
BlackBoxSourceHelper.writeFileList(resourceFiles ++ inlineFiles, targetDir)
state
}
}
object BlackBoxSourceHelper {
/**
* finds the named resource and writes into the directory
* @param name the name of the resource
* @param file the file to write it into
*/
def copyResourceToFile(name: String, file: File) {
val in = getClass.getResourceAsStream(name)
if (in == null) {
throw new FileNotFoundException(s"Resource '$name'")
}
val out = new FileOutputStream(file)
Iterator.continually(in.read).takeWhile(-1 != _).foreach(out.write)
out.close()
}
val fileListName = "firrtl_black_box_resource_files.f"
def writeFileList(files: ListSet[File], targetDir: File) {
if (files.nonEmpty) {
writeTextToFile(files.mkString("\n"), new File(targetDir, fileListName))
}
}
def writeTextToFile(text: String, file: File) {
val out = new PrintWriter(file)
out.write(text)
out.close()
}
}
|