Skip to content

Commit 3621072

Browse files
committed
1 parent 7271243 commit 3621072

8 files changed

Lines changed: 498 additions & 34 deletions

File tree

src/main/scala/chiseltest/iotesters/Driver.scala

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,16 +42,16 @@ object Driver {
4242
* @param testerGen A peek-poke tester with test for the dey
4343
* @return Returns true if all tests in testerGen pass
4444
*/
45-
def execute[T <: Module](args: Array[String], dut: () => T)(
45+
def execute[T <: Module](args: Array[String], dut: () => T, annos: Seq[Annotation] = List())(
4646
testerGen: T => PeekPokeTester[T]
4747
): Boolean = {
4848

49-
val annos = parseArgs(args)
49+
val inAnnos = annos ++: parseArgs(args)
5050

51-
val backendAnno = annos.collectFirst { case x: BackendAnnotation => x }.getOrElse(TreadleBackendAnnotation)
51+
val backendAnno = inAnnos.collectFirst { case x: BackendAnnotation => x }.getOrElse(TreadleBackendAnnotation)
5252

5353
// compile design
54-
val (highFirrtl, module) = Compiler.elaborate(() => dut(), annos)
54+
val (highFirrtl, module) = Compiler.elaborate(() => dut(), inAnnos)
5555

5656
// attach a target directory to place the firrtl in
5757
val highFirrtlWithTargetDir = defaultTargetDir(highFirrtl, getTesterName(testerGen))
@@ -67,7 +67,7 @@ object Driver {
6767

6868
// run tests
6969
val result = testContext.withValue(Some(localCtx)) {
70-
Logger.makeScope(annos) {
70+
Logger.makeScope(inAnnos) {
7171
testerGen(module).finish
7272
}
7373
}
@@ -141,14 +141,15 @@ object Driver {
141141
dutGen: () => T,
142142
backendType: String = "firrtl",
143143
verbose: Boolean = false,
144-
testerSeed: Long = System.currentTimeMillis())(
144+
testerSeed: Long = System.currentTimeMillis(),
145+
annos: Seq[Annotation] = List())(
145146
testerGen: T => PeekPokeTester[T]): Boolean = {
146147

147148
val args = List(
148149
"--backend-name", backendType,
149150
"--test-seed", testerSeed.toString,
150151
) ++ (if(verbose) List("--is-verbose") else List())
151-
execute(args.toArray, dutGen)(testerGen)
152+
execute(args.toArray, dutGen, annos = annos)(testerGen)
152153
}
153154

154155
private def backendNameToAnnotation(name: String): Annotation = name match {

src/main/scala/chiseltest/simulator/Simulator.scala

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,18 @@ trait SimulatorContext {
2121
def poke(signal: String, value: BigInt): Unit
2222
def peekMemory(memory: String, index: Long): BigInt
2323
def pokeMemory(memory: String, index: Long, value: BigInt): Unit
24-
// TODO: add reset coverage
25-
def getCoverage: List[(String, Long)]
2624
def finish(): SimulatorResults
2725
// for possible optimizations
2826
def peekLong(signal: String): Long = peek(signal).toLong
2927
def poke(signal: String, value: Long): Unit = poke(signal, BigInt(value))
3028
def peekMemoryLong(memory: String, index: Long): Long = peekMemory(memory, index).toLong
3129
def pokeMemory(memory: String, index: Long, value: Long): Unit = pokeMemory(memory, index, BigInt(value))
30+
// for fuzzing
31+
// def getCoverage(): List[(String, Long)]
32+
// def resetCoverage(): Unit
3233
}
3334

34-
case class SimulatorResults(exitCode: Int, waveformFile: Option[os.Path])
35+
case class SimulatorResults(exitCode: Int)
3536

3637
/** a firrtl circuit simulator */
3738
trait Simulator {

src/main/scala/chiseltest/simulator/TreadleSimulator.scala

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ private class TreadleContext(tester: TreadleTester) extends SimulatorContext {
5151
} catch {
5252
case s : StopException =>
5353
val exitCode = 1 // TODO!
54-
Some(SimulatorResults(exitCode, waveformFile))
54+
Some(SimulatorResults(exitCode))
5555
}
5656
}
5757

@@ -71,13 +71,9 @@ private class TreadleContext(tester: TreadleTester) extends SimulatorContext {
7171
tester.pokeMemory(memory, index.toInt, value)
7272
}
7373

74-
override def getCoverage: List[(String, Long)] = {
75-
tester.getCoverage()
76-
}
77-
7874
override def finish(): SimulatorResults = {
7975
tester.finish
80-
SimulatorResults(0, waveformFile)
76+
SimulatorResults(0)
8177
}
8278

8379
private def waveformFile: Option[os.Path] = if(tester.engine.vcdFileName.isEmpty) { None } else {

src/main/scala/chiseltest/simulator/VerilatorSimulator.scala

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,19 @@
22

33
package chiseltest.simulator
44

5-
import chiseltest.legacy.backends.verilator.CopyVerilatorHeaderFiles.getClass
65
import chiseltest.legacy.backends.verilator.{VerilatorCFlags, VerilatorFlags}
76
import chiseltest.simulator.ipc.{IPCSimulatorContext, VerilatorCppHarnessGenerator}
7+
import chiseltest.simulator.jni.{JNISimulatorContext, JNIUtils, VerilatorCppJNIHarnessGenerator}
88
import firrtl._
9+
import firrtl.annotations.{Annotation, NoTargetAnnotation}
910

1011
import java.io.{File, IOException}
1112
import java.nio.file.StandardCopyOption.REPLACE_EXISTING
1213
import java.nio.file.{FileAlreadyExistsException, Files, Path, Paths}
1314
import scala.sys.process._
1415

16+
private [chiseltest] case object VerilatorUseJNI extends NoTargetAnnotation
17+
1518
object VerilatorSimulator extends Simulator {
1619
override def name: String = "verilator"
1720

@@ -53,14 +56,16 @@ object VerilatorSimulator extends Simulator {
5356
val targetDir = chiseltest.dut.Compiler.requireTargetDir(state.annotations)
5457
val toplevel = TopmoduleInfo(state.circuit)
5558

59+
val useJNI = state.annotations.contains(VerilatorUseJNI)
60+
5661
// Create the header files that verilator needs + a custom harness
57-
val cppHarness = generateHarness(targetDir, toplevel)
62+
val cppHarness = generateHarness(targetDir, toplevel, useJNI)
5863

5964
// compile low firrtl to System Verilog for verilator to use
6065
chiseltest.dut.Compiler.lowFirrtlToSystemVerilog(state, VerilatorCoverage.CoveragePasses)
6166

6267
// turn SystemVerilog into C++ simulation
63-
val verilatedDir = runVerilator(state.circuit.main, targetDir, cppHarness, state.annotations)
68+
val verilatedDir = runVerilator(state.circuit.main, targetDir, cppHarness, state.annotations, useJNI)
6469

6570
// patch the coverage cpp provided with verilator only if Verilator is older than 4.202
6671
// Starting with Verilator 4.202, the whole patch coverage hack is no longer necessary
@@ -74,7 +79,11 @@ object VerilatorSimulator extends Simulator {
7479

7580
// the binary we created communicates using our standard IPC interface
7681
// TODO: waveform file + getCoverage!
77-
new IPCSimulatorContext(List(simBin.toString()), toplevel, VerilatorSimulator)
82+
if(useJNI) {
83+
new JNISimulatorContext(List(simBin.toString()), toplevel, VerilatorSimulator)
84+
} else {
85+
new IPCSimulatorContext(List(simBin.toString()), toplevel, VerilatorSimulator)
86+
}
7887
}
7988

8089
private def compileSimulation(topName: String, verilatedDir: os.Path): os.Path = {
@@ -88,12 +97,13 @@ object VerilatorSimulator extends Simulator {
8897
}
8998

9099
/** executes verilator in order to generate a C++ simulation */
91-
private def runVerilator(topName: String, targetDir: String, cppHarness: String, annos: AnnotationSeq): os.Path = {
100+
private def runVerilator(topName: String, targetDir: String, cppHarness: String, annos: AnnotationSeq, useJNI: Boolean): os.Path = {
92101
val targetDirPath = os.pwd / os.RelPath(targetDir)
93102
val verilatedDir = targetDirPath / "verilated"
94103

95104
removeOldCode(verilatedDir)
96-
val flags = generateFlags(topName, verilatedDir, annos)
105+
val flagAnnos: Seq[Annotation] = if(useJNI) { VerilatorCFlags(JNIUtils.ccFlags) +: annos } else { annos }
106+
val flags = generateFlags(topName, verilatedDir, flagAnnos)
97107
val cmd = List("verilator", "--cc", "--exe", cppHarness) ++ flags ++ List(s"$topName.sv")
98108
val ret = os.proc(cmd).call(cwd = targetDirPath)
99109

@@ -148,16 +158,19 @@ object VerilatorSimulator extends Simulator {
148158
flags
149159
}
150160

151-
private def generateHarness(targetDir: String, toplevel: TopmoduleInfo): String = {
161+
private def generateHarness(targetDir: String, toplevel: TopmoduleInfo, useJNI: Boolean): String = {
152162
val targetDirPath = os.pwd / os.RelPath(targetDir)
153163
val topName = toplevel.name
154164

155165
// Create the header files that verilator needs + a custom harness
156166
CopyVerilatorHeaderFiles(targetDir)
157167
val cppHarnessFileName = s"${topName}-harness.cpp"
158168
val vcdFile = new File(targetDir, s"$topName.vcd")
159-
val emittedStuff = VerilatorCppHarnessGenerator.codeGen(toplevel, vcdFile.toString, targetDir,
160-
majorVersion = majorVersion, minorVersion = minorVersion)
169+
val emittedStuff = if(useJNI) {
170+
VerilatorCppJNIHarnessGenerator.codeGen(toplevel, vcdFile.toString, targetDir, majorVersion = majorVersion, minorVersion = minorVersion)
171+
} else {
172+
VerilatorCppHarnessGenerator.codeGen(toplevel, vcdFile.toString, targetDir, majorVersion = majorVersion, minorVersion = minorVersion)
173+
}
161174
os.write.over(targetDirPath / cppHarnessFileName, emittedStuff)
162175

163176
cppHarnessFileName

src/main/scala/chiseltest/simulator/ipc/IPCSimulatorContext.scala

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ import scala.sys.process._
2525
* @param sim simulator that generated the binary
2626
* */
2727
private [chiseltest] class IPCSimulatorContext(cmd: Seq[String], toplevel: TopmoduleInfo,
28-
waveformFile: Option[os.Path], loadCoverage: () => List[(String, Long)],
2928
override val sim: Simulator) extends SimulatorContext with LazyLogging {
3029
require(toplevel.clocks.size == 1, "currently this interface only works with exactly one clock")
3130

@@ -300,7 +299,7 @@ private [chiseltest] class IPCSimulatorContext(cmd: Seq[String], toplevel: Topmo
300299
}
301300

302301
override def poke(signal: String, value: BigInt) {
303-
if (inputsNameToChunkSizeMap contains signal) {
302+
if (inputsNameToChunkSizeMap.contains(signal)) {
304303
_pokeMap(signal) = value
305304
isStale = true
306305
} else {
@@ -346,7 +345,7 @@ private [chiseltest] class IPCSimulatorContext(cmd: Seq[String], toplevel: Topmo
346345
None
347346
} catch {
348347
case TestApplicationException(exit, msg) =>
349-
Some(SimulatorResults(exit, waveformFile))
348+
Some(SimulatorResults(exit))
350349
}
351350
}
352351

@@ -360,7 +359,7 @@ private [chiseltest] class IPCSimulatorContext(cmd: Seq[String], toplevel: Topmo
360359
outChannel.close()
361360
cmdChannel.close()
362361
isRunning = false
363-
SimulatorResults(exit, waveformFile)
362+
SimulatorResults(exit)
364363
}
365364

366365
// Once everything has been prepared, we can start the communications.
@@ -373,11 +372,6 @@ private [chiseltest] class IPCSimulatorContext(cmd: Seq[String], toplevel: Topmo
373372
override def pokeMemory(memory: String, index: Long, value: BigInt): Unit = {
374373
throw new NotImplementedError("pokeMemory")
375374
}
376-
377-
override def getCoverage: List[(String, Long)] = {
378-
require(!isRunning, "Cannot get coverage while the simulation is running!")
379-
loadCoverage()
380-
}
381375
}
382376

383377

0 commit comments

Comments
 (0)