Skip to content
This repository has been archived by the owner on Jun 16, 2024. It is now read-only.

Commit

Permalink
generalise compilation steps.
Browse files Browse the repository at this point in the history
  • Loading branch information
kivikakk committed May 14, 2024
1 parent 946a52f commit fb93e39
Show file tree
Hide file tree
Showing 4 changed files with 169 additions and 28 deletions.
6 changes: 5 additions & 1 deletion src/main/scala/ee/hrzn/chryse/ChryseApp.scala
Original file line number Diff line number Diff line change
Expand Up @@ -79,13 +79,15 @@ abstract class ChryseApp {
val setup = new DefaultOParserSetup {
override def showUsageOnError = Some(true)
}
var terminating = false
val config =
OParser.runParser(parser, args, ChryseAppConfig(), setup) match {
case (result, effects) =>
OParser.runEffects(
effects,
new DefaultOEffectSetup {
override def terminate(exitState: Either[String, Unit]): Unit = ()
override def terminate(exitState: Either[String, Unit]): Unit =
terminating = true
},
)

Expand All @@ -95,6 +97,8 @@ abstract class ChryseApp {
}
}

if (terminating) return

println(
s"$name ${getClass().getPackage().getImplementationVersion()} " +
s"(Chryse ${ChryseApp.getChrysePackage().getImplementationVersion()})",
Expand Down
82 changes: 78 additions & 4 deletions src/main/scala/ee/hrzn/chryse/tasks/BaseTask.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ package ee.hrzn.chryse.tasks
import ee.hrzn.chryse.ChryseAppStepFailureException

import java.io.PrintWriter
import java.nio.file.Files
import java.nio.file.Paths
import java.security.MessageDigest
import java.util.HexFormat
import scala.sys.process._

abstract class BaseTask {
Expand All @@ -14,17 +18,87 @@ abstract class BaseTask {
"-strip-debug-info",
)

protected def writeCare(path: String, content: String): Unit = {
protected def writePath(path: String, content: String): Unit = {
new PrintWriter(path, "utf-8") {
try write(content)
finally close()
}
}

protected def runCare(step: String, cmd: Seq[String]): Unit = {
val result = cmd.!
if (result != 0) {
protected def runCmd(step: String, cmd: Seq[String]) =
runCmds(step, Seq(cmd))

protected def runCmds(
step: String,
cmds: Iterable[Seq[String]],
): Unit = {
cmds.foreach(cmd => println(s"($step) running: $cmd"))
val processes = cmds.map(cmd => (cmd, cmd.run()))
val failed = processes.collect {
case (cmd, proc) if proc.exitValue() != 0 => cmd
}
if (!failed.isEmpty) {
println("the following process(es) failed:")
for { cmd <- failed } println(s" $cmd")
throw new ChryseAppStepFailureException(step)
}
}

protected def runCu(step: String, cu: CompilationUnit) =
runCus(step, Seq(cu))

protected def runCus(
step: String,
cus: Iterable[CompilationUnit],
): Unit = {
val (skip, run) = cus.partition(_.isUpToDate())
skip.foreach(cu => println(s"($step) skipping: ${cu.cmd}"))
runCmds(step, run.map(_.cmd))
run.foreach(_.markUpToDate())
}

case class CompilationUnit(
val inPaths: Seq[String],
val outPath: String,
val cmd: Seq[String],
) {
val digestPath = s"$outPath.dig"
private val sortedInPaths = inPaths.sorted

private def addIntToDigest(n: Int)(implicit digest: MessageDigest): Unit =
digest.update(String.format("%08x", n).getBytes("UTF-8"))

private def addStringToDigest(s: String)(implicit
digest: MessageDigest,
): Unit =
addBytesToDigest(s.getBytes("UTF-8"))

private def addBytesToDigest(
b: Array[Byte],
)(implicit digest: MessageDigest): Unit = {
addIntToDigest(b.length)
digest.update(b)
}

private def digestInsWithCmd(): String = {
implicit val digest = MessageDigest.getInstance("SHA-256")
addIntToDigest(inPaths.length)
for { inPath <- inPaths.sorted } {
addStringToDigest(inPath)
addBytesToDigest(Files.readAllBytes(Paths.get(inPath)))
}
addIntToDigest(cmd.length)
for { el <- cmd }
addStringToDigest(el)
HexFormat.of().formatHex(digest.digest())
}

def isUpToDate(): Boolean =
Files.exists(Paths.get(digestPath)) && Files.readString(
Paths.get(digestPath),
) == digestInsWithCmd()

def markUpToDate(): Unit =
writePath(digestPath, digestInsWithCmd())
}
}
27 changes: 17 additions & 10 deletions src/main/scala/ee/hrzn/chryse/tasks/BuildTask.scala
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,20 @@ object BuildTask extends BaseTask {
platform(genTop(platform)),
firtoolOpts = firtoolOpts,
)
writeCare(verilogPath, verilog)
writePath(verilogPath, verilog)

val yosysScriptPath = s"$buildDir/$name-${platform.id}.ys"
val jsonPath = s"$buildDir/$name-${platform.id}.json"
writeCare(
writePath(
yosysScriptPath,
s"""read_verilog -sv $verilogPath
|synth_ice40 -top top
|write_json $jsonPath""".stripMargin,
)

runCare(
"synthesis",
val yosysCu = CompilationUnit(
Seq(verilogPath, yosysScriptPath),
jsonPath,
Seq(
"yosys",
"-q",
Expand All @@ -49,12 +50,15 @@ object BuildTask extends BaseTask {
yosysScriptPath,
),
)
runCu("synthesis", yosysCu)

// TODO: generate PCF.
val pcfPath = s"$name-${platform.id}.pcf"

val ascPath = s"$buildDir/$name-${platform.id}.asc"
runCare(
"place and route",
val ascCu = CompilationUnit(
Seq(jsonPath, pcfPath),
ascPath,
Seq(
platform.nextpnrBinary,
"-q",
Expand All @@ -63,21 +67,24 @@ object BuildTask extends BaseTask {
"--json",
jsonPath,
"--pcf",
s"$name-${platform.id}.pcf",
pcfPath,
"--asc",
ascPath,
) ++ platform.nextpnrArgs,
)
runCu("place and route", ascCu)

val binPath = s"$buildDir/$name-${platform.id}.bin"
runCare(
"pack",
val binCu = CompilationUnit(
Seq(ascPath),
binPath,
Seq(platform.packBinary, ascPath, binPath),
)
runCu("pack", binCu)

if (program) {
println(s"Programming ${platform.id} ...")
runCare("program", Seq(platform.programBinary, binPath))
runCmd("program", Seq(platform.programBinary, binPath))
}
}
}
82 changes: 69 additions & 13 deletions src/main/scala/ee/hrzn/chryse/tasks/CxxsimTask.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,19 @@ import ee.hrzn.chryse.platform.Platform
import ee.hrzn.chryse.platform.cxxrtl.CXXRTLOptions
import ee.hrzn.chryse.platform.cxxrtl.CXXRTLPlatform

import java.nio.file.FileVisitOption
import java.nio.file.Files
import java.nio.file.Paths
import scala.jdk.CollectionConverters._
import scala.sys.process._

object CxxsimTask extends BaseTask {
private val cxxsimDir = "cxxsim"
private val cxxOpts = Seq("-std=c++14", "-g", "-pedantic", "-Wall", "-Wextra",
"-Wno-zero-length-array", "-Wno-unused-parameter")

def apply(
name: String,
genTop: Platform => HasIO[_ <: chisel3.Data],
genTop: Platform => HasIO[_ <: Data],
cxxrtlOptions: CXXRTLOptions,
config: ChryseAppConfig,
): Unit = {
Expand All @@ -34,23 +36,24 @@ object CxxsimTask extends BaseTask {
platform(genTop(platform)),
firtoolOpts = firtoolOpts,
)
writeCare(verilogPath, verilog)
writePath(verilogPath, verilog)

val blackboxIlPath = s"$buildDir/$name-${platform.id}-blackbox.il"
// TODO
writeCare(blackboxIlPath, "\n")
writePath(blackboxIlPath, "\n")

val yosysScriptPath = s"$buildDir/$name-${platform.id}.ys"
val ccPath = s"$buildDir/$name.cc"
writeCare(
writePath(
yosysScriptPath,
s"""read_rtlil $blackboxIlPath
|read_verilog -sv $verilogPath
|write_cxxrtl -header $ccPath""".stripMargin,
)

runCare(
"synthesis",
val yosysCu = CompilationUnit(
Seq(blackboxIlPath, verilogPath, yosysScriptPath),
ccPath,
Seq(
"yosys",
"-q",
Expand All @@ -61,15 +64,68 @@ object CxxsimTask extends BaseTask {
yosysScriptPath,
),
)

// Compile all sources in cxxsimDir.
// Use Make-like heuristics to determine what's up to date.
// TODO: continue here.
for { p <- Files.walk(Paths.get(cxxsimDir)).iterator.asScala }
println(s"p: $p")
runCu("synthesis", yosysCu)

// TODO: we need to decide how the simulation gets driven. How do we offer
// enough control to the user? Do we assume they/let them do all the setup
// themselves? etc.
//
// Fundamentally, the user may have many different ways of driving the
// process. We want to facilitate connecting blackboxes etc., but what else?
// Hrmmm. Let's start simple (just compiling everything, like rainhdx), and
// then see where we go.
val ccs = Seq(ccPath) ++ filesInDirWithExt(cxxsimDir, ".cc")
val headers = filesInDirWithExt(cxxsimDir, ".h").toSeq

val yosysDatDir = Seq("yosys-config", "--datdir").!!.trim()

def buildPathForCc(cc: String) =
cc.replace(s"$cxxsimDir/", s"$buildDir/")
.replace(".cc", ".o")

def compileCmdForCc(cc: String, obj: String) = Seq(
"c++",
s"-I$buildDir",
s"-I$yosysDatDir/include/backends/cxxrtl/runtime",
"-c",
cc,
"-o",
obj,
) ++ cxxOpts

// XXX: depend on what look like headers for now.
val cus = for {
cc <- ccs
obj = buildPathForCc(cc)
cmd = compileCmdForCc(cc, obj)
} yield CompilationUnit(Seq(cc) ++ headers, obj, cmd)

runCus("compilation", cus)

val binPath = s"$buildDir/$name"
val linkCu = CompilationUnit(
cus.map(_.outPath),
binPath,
Seq("c++", "-o", binPath) ++ cxxOpts ++ cus.map(_.outPath),
)
runCu("linking", linkCu)

if (config.cxxrtlCompileOnly) return

val binArgs = if (config.cxxrtlVcd) Seq("--vcd") else Seq()
val binCmd = Seq(binPath) ++ binArgs

println(s"running: $binCmd")
val rc = binCmd.!

println(s"$name exited with return code $rc")
}

private def filesInDirWithExt(dir: String, ext: String): Iterator[String] =
Files
.walk(Paths.get(dir), 1)
.iterator
.asScala
.map(_.toString)
.filter(_.endsWith(ext))
}

0 comments on commit fb93e39

Please sign in to comment.