spinal.sim.VpiBackend.scala Maven / Gradle / Ivy
The newest version!
package spinal.sim
import java.nio.file.{Paths, Files}
import java.io.{File, PrintWriter}
import java.lang.Thread
import scala.io.Source
import org.apache.commons.io.FileUtils
import scala.collection.mutable.ArrayBuffer
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
import scala.sys.process._
import scala.util.Properties
import spinal.sim.vpi._
case class VpiBackendConfig(
val rtlIncludeDirs : ArrayBuffer[String] = ArrayBuffer[String](),
val rtlSourcesPaths: ArrayBuffer[String] = ArrayBuffer[String](),
var toplevelName: String = null,
var pluginsPath: String = "simulation_plugins",
var workspacePath: String = null,
var workspaceName: String = null,
var wavePath: String = null,
var wavePrefix: String = null,
var waveFormat: WaveFormat = WaveFormat.NONE,
var analyzeFlags: String = "",
var runFlags: String = "",
var sharedMemSize: Int = 65536,
var CC: String = "g++",
var CFLAGS: String = "-std=c++14 -Wall -Wextra -pedantic -O2 -Wno-strict-aliasing -Wno-write-strings",
var LDFLAGS: String = "-lpthread ",
var useCache: Boolean = false,
var logSimProcess: Boolean = false,
var timePrecision: String = null,
var testPath: String = null
)
abstract class VpiBackend(val config: VpiBackendConfig) extends Backend {
import Backend._
val rtlSourcesPaths = config.rtlSourcesPaths
val toplevelName = config.toplevelName
val pluginsPath = config.pluginsPath
val workspacePath = config.workspacePath
val workspaceName = config.workspaceName
val wavePath = config.wavePath
val waveFormat = config.waveFormat
val analyzeFlags = config.analyzeFlags
var runFlags = config.runFlags
val sharedMemSize = config.sharedMemSize
val CC = config.CC
var CFLAGS = config.CFLAGS
var LDFLAGS = config.LDFLAGS
val useCache = config.useCache
val logSimProcess = config.logSimProcess
val sharedExtension = if(isWindows) "dll" else (if(isMac) "dylib" else "so")
val sharedMemIfaceName = "shared_mem_iface." + sharedExtension
val sharedMemIfacePath = pluginsPath + "/" + sharedMemIfaceName
var runIface = 0
CFLAGS += " -fPIC -DNDEBUG -I " + pluginsPath
LDFLAGS += (if(!isMac) " -shared" else "")
LDFLAGS += (if(!isWindows && !isMac) " -lrt" else "")
val jdk = System.getProperty("java.home").replace("/jre","").replace("\\jre","")
val includes = Seq(
s"$jdk/include",
s"$jdk/include/${(if(isWindows) "win32"
else (if (isMac) "darwin"
else "linux"))}"
)
CFLAGS += " " + includes.map(path =>
if (isWindows)
'"' + path + '"'
else
path
).map(path => s"-I$path").mkString(" ") + " "
def doCmd(command: String, cwd: File, message : String) = {
val logger = new Logger()
if(Process(command, cwd)! (logger) != 0){
println(logger.logs)
throw new Exception(message)
}
}
def doCmd(command: String, cwd: File, extraEnv: Seq[(String, String)], message : String) = {
val logger = new Logger()
if(Process(command, cwd, extraEnv :_*)! (logger) != 0){
println(logger.logs)
throw new Exception(message)
}
}
class Logger extends ProcessLogger {
val logs = new StringBuilder()
override def err(s: => String): Unit = { logs ++= (s ++ Properties.lineSeparator) }
override def out(s: => String): Unit = { logs ++= (s ++ Properties.lineSeparator) }
override def buffer[T](f: => T) = f
}
class LoggerPrint extends ProcessLogger {
override def err(s: => String): Unit = { println(s) }
override def out(s: => String): Unit = { println(s) }
override def buffer[T](f: => T) = f
}
val pwd = new File(".").getAbsolutePath().mkString
lazy val delayed_compilation: Unit = {
if(!(Files.exists(Paths.get(sharedMemIfacePath)) && useCache)) {
List("/SharedMemIface.cpp",
"/SharedMemIface.hpp",
"/SharedMemIface_wrap.cxx",
"/SharedStruct.hpp").foreach { filename =>
val cppSourceFile = new PrintWriter(new File(pluginsPath + "/" + filename))
val stream = getClass.getResourceAsStream(filename)
cppSourceFile.write(scala.io.Source.fromInputStream(stream).mkString)
cppSourceFile.close
}
doCmd(
Seq(CC,
"-c",
CFLAGS,
"SharedMemIface.cpp",
"-o",
"SharedMemIface.o"
).mkString(" "),
new File(pluginsPath),
"Compilation of SharedMemIface.cpp failed"
)
doCmd(
Seq(CC,
"-c",
CFLAGS,
"SharedMemIface_wrap.cxx",
"-o",
"SharedMemIface_wrap.o"
).mkString(" "),
new File(pluginsPath),
"Compilation of SharedMemIface_wrap.cxx failed"
)
doCmd(
Seq(CC,
CFLAGS + (if(isMac) " -dynamiclib " else ""),
"SharedMemIface.o",
"SharedMemIface_wrap.o",
LDFLAGS,
"-o",
sharedMemIfaceName
).mkString(" "),
new File(pluginsPath),
"Compilation of SharedMemIface." + sharedExtension + " failed")
}
System.load(pwd + "/" + sharedMemIfacePath)
compileVPI()
analyzeRTL()
}
def clean() : Unit = {
FileUtils.deleteQuietly(new File(s"${workspacePath}/${workspaceName}"))
FileUtils.cleanDirectory(new File(pluginsPath))
}
def compileVPI() : Unit // Return the plugin name
def analyzeRTL() : Unit
def runSimulation(sharedMemIface: SharedMemIface, testName: String) : Thread
def instanciate_(name: String) : (SharedMemIface, Thread) = {
delayed_compilation
val shmemKey = Seq("SpinalHDL",
runIface.toString,
uniqueId.toString,
hashCode().toString,
pwd.hashCode().toString,
System.currentTimeMillis().toString,
scala.util.Random.nextLong().toString).mkString("_")
runIface += 1
val sharedMemIface = new SharedMemIface(shmemKey, sharedMemSize)
var shmemFile = new PrintWriter(new File(workspacePath + "/shmem_name"))
shmemFile.write(shmemKey)
shmemFile.close
val thread = runSimulation(sharedMemIface, name)
sharedMemIface.check_ready
(sharedMemIface, thread)
}
def instanciate(name: String, seed : Long) : (SharedMemIface, Thread) = {
val ret = if(useCache) {
VpiBackend.synchronized {
instanciate_(name)
}
} else {
this.synchronized {
instanciate_(name)
}
}
ret._1.set_seed(seed)
ret._1.eval
ret
}
def instanciate(name: String) : (SharedMemIface, Thread) = instanciate(name, 0x5EED5EED)
}
object VpiBackend {}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy