Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
ekiwi committed Jun 28, 2021
1 parent 7271243 commit 3621072
Show file tree
Hide file tree
Showing 8 changed files with 498 additions and 34 deletions.
15 changes: 8 additions & 7 deletions src/main/scala/chiseltest/iotesters/Driver.scala
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,16 @@ object Driver {
* @param testerGen A peek-poke tester with test for the dey
* @return Returns true if all tests in testerGen pass
*/
def execute[T <: Module](args: Array[String], dut: () => T)(
def execute[T <: Module](args: Array[String], dut: () => T, annos: Seq[Annotation] = List())(
testerGen: T => PeekPokeTester[T]
): Boolean = {

val annos = parseArgs(args)
val inAnnos = annos ++: parseArgs(args)

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

// compile design
val (highFirrtl, module) = Compiler.elaborate(() => dut(), annos)
val (highFirrtl, module) = Compiler.elaborate(() => dut(), inAnnos)

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

// run tests
val result = testContext.withValue(Some(localCtx)) {
Logger.makeScope(annos) {
Logger.makeScope(inAnnos) {
testerGen(module).finish
}
}
Expand Down Expand Up @@ -141,14 +141,15 @@ object Driver {
dutGen: () => T,
backendType: String = "firrtl",
verbose: Boolean = false,
testerSeed: Long = System.currentTimeMillis())(
testerSeed: Long = System.currentTimeMillis(),
annos: Seq[Annotation] = List())(
testerGen: T => PeekPokeTester[T]): Boolean = {

val args = List(
"--backend-name", backendType,
"--test-seed", testerSeed.toString,
) ++ (if(verbose) List("--is-verbose") else List())
execute(args.toArray, dutGen)(testerGen)
execute(args.toArray, dutGen, annos = annos)(testerGen)
}

private def backendNameToAnnotation(name: String): Annotation = name match {
Expand Down
7 changes: 4 additions & 3 deletions src/main/scala/chiseltest/simulator/Simulator.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,18 @@ trait SimulatorContext {
def poke(signal: String, value: BigInt): Unit
def peekMemory(memory: String, index: Long): BigInt
def pokeMemory(memory: String, index: Long, value: BigInt): Unit
// TODO: add reset coverage
def getCoverage: List[(String, Long)]
def finish(): SimulatorResults
// for possible optimizations
def peekLong(signal: String): Long = peek(signal).toLong
def poke(signal: String, value: Long): Unit = poke(signal, BigInt(value))
def peekMemoryLong(memory: String, index: Long): Long = peekMemory(memory, index).toLong
def pokeMemory(memory: String, index: Long, value: Long): Unit = pokeMemory(memory, index, BigInt(value))
// for fuzzing
// def getCoverage(): List[(String, Long)]
// def resetCoverage(): Unit
}

case class SimulatorResults(exitCode: Int, waveformFile: Option[os.Path])
case class SimulatorResults(exitCode: Int)

/** a firrtl circuit simulator */
trait Simulator {
Expand Down
8 changes: 2 additions & 6 deletions src/main/scala/chiseltest/simulator/TreadleSimulator.scala
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ private class TreadleContext(tester: TreadleTester) extends SimulatorContext {
} catch {
case s : StopException =>
val exitCode = 1 // TODO!
Some(SimulatorResults(exitCode, waveformFile))
Some(SimulatorResults(exitCode))
}
}

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

override def getCoverage: List[(String, Long)] = {
tester.getCoverage()
}

override def finish(): SimulatorResults = {
tester.finish
SimulatorResults(0, waveformFile)
SimulatorResults(0)
}

private def waveformFile: Option[os.Path] = if(tester.engine.vcdFileName.isEmpty) { None } else {
Expand Down
31 changes: 22 additions & 9 deletions src/main/scala/chiseltest/simulator/VerilatorSimulator.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,19 @@

package chiseltest.simulator

import chiseltest.legacy.backends.verilator.CopyVerilatorHeaderFiles.getClass
import chiseltest.legacy.backends.verilator.{VerilatorCFlags, VerilatorFlags}
import chiseltest.simulator.ipc.{IPCSimulatorContext, VerilatorCppHarnessGenerator}
import chiseltest.simulator.jni.{JNISimulatorContext, JNIUtils, VerilatorCppJNIHarnessGenerator}
import firrtl._
import firrtl.annotations.{Annotation, NoTargetAnnotation}

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

private [chiseltest] case object VerilatorUseJNI extends NoTargetAnnotation

object VerilatorSimulator extends Simulator {
override def name: String = "verilator"

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

val useJNI = state.annotations.contains(VerilatorUseJNI)

// Create the header files that verilator needs + a custom harness
val cppHarness = generateHarness(targetDir, toplevel)
val cppHarness = generateHarness(targetDir, toplevel, useJNI)

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

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

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

// the binary we created communicates using our standard IPC interface
// TODO: waveform file + getCoverage!
new IPCSimulatorContext(List(simBin.toString()), toplevel, VerilatorSimulator)
if(useJNI) {
new JNISimulatorContext(List(simBin.toString()), toplevel, VerilatorSimulator)
} else {
new IPCSimulatorContext(List(simBin.toString()), toplevel, VerilatorSimulator)
}
}

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

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

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

Expand Down Expand Up @@ -148,16 +158,19 @@ object VerilatorSimulator extends Simulator {
flags
}

private def generateHarness(targetDir: String, toplevel: TopmoduleInfo): String = {
private def generateHarness(targetDir: String, toplevel: TopmoduleInfo, useJNI: Boolean): String = {
val targetDirPath = os.pwd / os.RelPath(targetDir)
val topName = toplevel.name

// Create the header files that verilator needs + a custom harness
CopyVerilatorHeaderFiles(targetDir)
val cppHarnessFileName = s"${topName}-harness.cpp"
val vcdFile = new File(targetDir, s"$topName.vcd")
val emittedStuff = VerilatorCppHarnessGenerator.codeGen(toplevel, vcdFile.toString, targetDir,
majorVersion = majorVersion, minorVersion = minorVersion)
val emittedStuff = if(useJNI) {
VerilatorCppJNIHarnessGenerator.codeGen(toplevel, vcdFile.toString, targetDir, majorVersion = majorVersion, minorVersion = minorVersion)
} else {
VerilatorCppHarnessGenerator.codeGen(toplevel, vcdFile.toString, targetDir, majorVersion = majorVersion, minorVersion = minorVersion)
}
os.write.over(targetDirPath / cppHarnessFileName, emittedStuff)

cppHarnessFileName
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import scala.sys.process._
* @param sim simulator that generated the binary
* */
private [chiseltest] class IPCSimulatorContext(cmd: Seq[String], toplevel: TopmoduleInfo,
waveformFile: Option[os.Path], loadCoverage: () => List[(String, Long)],
override val sim: Simulator) extends SimulatorContext with LazyLogging {
require(toplevel.clocks.size == 1, "currently this interface only works with exactly one clock")

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

override def poke(signal: String, value: BigInt) {
if (inputsNameToChunkSizeMap contains signal) {
if (inputsNameToChunkSizeMap.contains(signal)) {
_pokeMap(signal) = value
isStale = true
} else {
Expand Down Expand Up @@ -346,7 +345,7 @@ private [chiseltest] class IPCSimulatorContext(cmd: Seq[String], toplevel: Topmo
None
} catch {
case TestApplicationException(exit, msg) =>
Some(SimulatorResults(exit, waveformFile))
Some(SimulatorResults(exit))
}
}

Expand All @@ -360,7 +359,7 @@ private [chiseltest] class IPCSimulatorContext(cmd: Seq[String], toplevel: Topmo
outChannel.close()
cmdChannel.close()
isRunning = false
SimulatorResults(exit, waveformFile)
SimulatorResults(exit)
}

// Once everything has been prepared, we can start the communications.
Expand All @@ -373,11 +372,6 @@ private [chiseltest] class IPCSimulatorContext(cmd: Seq[String], toplevel: Topmo
override def pokeMemory(memory: String, index: Long, value: BigInt): Unit = {
throw new NotImplementedError("pokeMemory")
}

override def getCoverage: List[(String, Long)] = {
require(!isRunning, "Cannot get coverage while the simulation is running!")
loadCoverage()
}
}


Expand Down
Loading

0 comments on commit 3621072

Please sign in to comment.