-
Notifications
You must be signed in to change notification settings - Fork 51
Expand file tree
/
Copy pathChiselMain.scala
More file actions
205 lines (185 loc) · 7.65 KB
/
Copy pathChiselMain.scala
File metadata and controls
205 lines (185 loc) · 7.65 KB
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
// See LICENSE for license details.
package chisel3.iotesters
import java.io.{File, FileWriter, IOException}
import java.nio.file.{FileAlreadyExistsException, Files, Paths}
import scala.collection.mutable.ArrayBuffer
import scala.util.DynamicVariable
import chisel3._
private[iotesters] class TesterContext {
var isGenVerilog = false
var isGenHarness = false
var isCompiling = false
var isRunTest = false
var testerSeed: Long = System.currentTimeMillis
val testCmd: ArrayBuffer[String] = ArrayBuffer[String]()
var backendType = "verilator"
var backend: Option[Backend] = None
var targetDir = new File("test_run_dir")
var logFile: Option[File] = None
var waveform: Option[File] = None
}
object chiselMain {
private val contextVar = new DynamicVariable[Option[TesterContext]](None)
private[iotesters] def context = contextVar.value.getOrElse(new TesterContext)
private def parseArgs(args: List[String]): Unit = args match {
case "--firrtl" :: tail => context.backendType = "firrtl" ; parseArgs(tail)
case "--verilator" :: tail => context.backendType = "verilator" ; parseArgs(tail)
case "--vcs" :: tail => context.backendType = "vcs" ; parseArgs(tail)
case "--glsim" :: tail => context.backendType = "glsim" ; parseArgs(tail)
case "--v" :: tail => context.isGenVerilog = true ; parseArgs(tail)
case "--backend" :: value :: tail => context.backendType = value ; parseArgs(tail)
case "--genHarness" :: tail => context.isGenHarness = true ; parseArgs(tail)
case "--compile" :: tail => context.isCompiling = true ; parseArgs(tail)
case "--test" :: tail => context.isRunTest = true ; parseArgs(tail)
case "--testCommand" :: value :: tail => context.testCmd ++= value split ' ' ; parseArgs(tail)
case "--testerSeed" :: value :: tail => context.testerSeed = value.toLong ; parseArgs(tail)
case "--targetDir" :: value :: tail => context.targetDir = new File(value) ; parseArgs(tail)
case "--logFile" :: value :: tail => context.logFile = Some(new File(value)) ; parseArgs(tail)
case "--waveform" :: value :: tail => context.waveform = Some(new File(value)) ; parseArgs(tail)
case _ :: tail => parseArgs(tail) // skip unknown flag
case Nil => // finish
}
private def genHarness[T <: Module](dut: Module, nodes: Seq[internal.InstanceId], chirrtl: firrtl.ir.Circuit) {
import firrtl.{ChirrtlForm, CircuitState}
val dir = context.targetDir
context.backendType match {
case "firrtl" => // skip
case "verilator" =>
val harness = new FileWriter(new File(dir, s"${chirrtl.main}-harness.cpp"))
val waveform = new File(dir, s"${chirrtl.main}.vcd").toString
harness.write(VerilatorCppHarnessGenerator.codeGen(dut, CircuitState(chirrtl, ChirrtlForm), waveform))
harness.close()
case "ivl" =>
val harness = new FileWriter(new File(dir, s"${chirrtl.main}-harness.v"))
val waveform = new File(dir, s"${chirrtl.main}.vcd").toString
genIVLVerilogHarness(dut, harness, waveform.toString)
case "vcs" | "glsim" =>
val harness = new FileWriter(new File(dir, s"${chirrtl.main}-harness.v"))
val waveform = new File(dir, s"${chirrtl.main}.vpd").toString
genVCSVerilogHarness(dut, harness, waveform.toString, context.backendType == "glsim")
case b => throw BackendException(b)
}
}
private def compile(dutName: String) {
val dir = context.targetDir
context.backendType match {
case "firrtl" => // skip
case "verilator" =>
// Copy API files
copyVerilatorHeaderFiles(context.targetDir.toString)
// Generate Verilator
assert(chisel3.Driver.verilogToCpp(
dutName,
dir,
Seq(),
new File(dir, s"$dutName-harness.cpp")).! == 0)
// Compile Verilator
assert(setupVerilatorBackend.cppToSo(dutName, dir).! == 0)
case "vcs" | "glsim" =>
// Copy API files
copyVpiFiles(context.targetDir.toString)
// Compile VCS
assert(verilogToVCS(dutName, dir, new File(s"$dutName-harness.v")).! == 0)
case b => throw BackendException(b)
}
}
private def elaborate[T <: Module](args: Array[String], dutGen: () => T): T = {
parseArgs(args.toList)
try {
Files.createDirectory(Paths.get(context.targetDir.toString))
} catch {
case _: FileAlreadyExistsException =>
case x: IOException =>
System.err.format("createFile error: %s%n", x)
}
val circuit = chisel3.Driver.elaborate(dutGen)
val dut = getTopModule(circuit).asInstanceOf[T]
val nodes = getChiselNodes(circuit)
val dir = context.targetDir
val name = circuit.name
val chirrtl = firrtl.Parser.parse(chisel3.Driver.emit(circuit))
val chirrtlFile = new File(dir, s"$name.ir")
val verilogFile = new File(dir, s"$name.v")
context.backendType match {
case "firrtl" =>
val writer = new FileWriter(chirrtlFile)
(new firrtl.LowFirrtlEmitter).emit(firrtl.CircuitState(chirrtl, firrtl.ChirrtlForm), writer)
writer.close()
case _ if context.isGenVerilog =>
val annotations = Seq(firrtl.passes.memlib.InferReadWriteAnnotation)
val writer = new FileWriter(verilogFile)
val compileResult = (new firrtl.VerilogCompiler).compileAndEmit(
firrtl.CircuitState(chirrtl, firrtl.ChirrtlForm, annotations),
List(new firrtl.passes.memlib.InferReadWrite)
)
writer.write(compileResult.getEmittedCircuit.value)
writer.close()
case _ =>
}
if (context.isGenHarness) genHarness(dut, nodes, chirrtl)
if (context.isCompiling) compile(name)
dut
}
private def setupBackend[T <: Module](dut: T) {
val name = dut.name
if (context.testCmd.isEmpty) {
context.backendType match {
case "firrtl" => // skip
case "verilator" =>
context.testCmd += new File(context.targetDir, s"V$name").toString
case "vcs" | "glsim" =>
context.testCmd += new File(context.targetDir, name).toString
case b => throw BackendException(b)
}
}
context.waveform match {
case None =>
case Some(f) => context.testCmd += s"+waveform=$f"
}
context.backend = Some(context.backendType match {
case "firrtl" =>
val file = new java.io.File(context.targetDir, s"${dut.name}.ir")
val ir = io.Source.fromFile(file).getLines mkString "\n"
new FirrtlTerpBackend(dut, ir)
case "verilator" =>
new VerilatorBackend(dut, context.testCmd.toList, context.testerSeed)
case "vcs" | "glsim" =>
new VCSBackend(dut, context.testCmd.toList, context.testerSeed)
case b => throw BackendException(b)
})
}
def apply[T <: Module](args: Array[String], dutGen: () => T): T = {
val ctx = Some(new TesterContext)
val dut = contextVar.withValue(ctx) {
elaborate(args, dutGen)
}
dut
}
def apply[T <: Module](args: Array[String], dutGen: () => T, testerGen: T => PeekPokeTester[T]): Unit = {
contextVar.withValue(Some(new TesterContext)) {
val dut = elaborate(args, dutGen)
if (context.isRunTest) {
setupBackend(dut)
assert(try {
testerGen(dut).finish
} catch { case e: Throwable =>
e.printStackTrace()
context.backend match {
case Some(b: VCSBackend) =>
TesterProcess kill b
case Some(b: VerilatorBackend) =>
TesterProcess kill b
case _ =>
}
false
}, "Test failed")
}
dut
}
}
}
object chiselMainTest {
def apply[T <: Module](args: Array[String], dutGen: () => T)(testerGen: T => PeekPokeTester[T]): Unit = {
chiselMain(args, dutGen, testerGen)
}
}