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

Commit

Permalink
wip: tasks!
Browse files Browse the repository at this point in the history
  • Loading branch information
kivikakk committed May 12, 2024
1 parent 5861620 commit 4d45e56
Show file tree
Hide file tree
Showing 9 changed files with 281 additions and 139 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ its own HDL depending on how much I love Scala. :)
## TODO

* Generate PCFs on demand via board descriptions.
* CXXRTL/cxxsim.
* Parameterise correctly for ECP5.
* Remove sbtchryse if we don't need it after all.
* CXXRTL/cxxsim.
208 changes: 81 additions & 127 deletions src/main/scala/ee/hrzn/chryse/ChryseApp.scala
Original file line number Diff line number Diff line change
@@ -1,64 +1,79 @@
package ee.hrzn.chryse

import _root_.circt.stage.ChiselStage
import chisel3._
import ee.hrzn.chryse.platform.ElaboratablePlatform
import chisel3.Data
import circt.stage.ChiselStage
import ee.hrzn.chryse.platform.BoardPlatform
import ee.hrzn.chryse.platform.Platform

import java.io.PrintWriter
import scala.sys.process._
import scopt.OParser
import ee.hrzn.chryse.platform.cxxrtl.CXXRTLOptions
import scopt.DefaultOEffectSetup
import scopt.DefaultOParserSetup
import ee.hrzn.chryse.platform.BoardPlatform
import java.nio.file.Files
import java.nio.file.Paths
import scopt.OParser

import scala.collection.mutable

abstract class ChryseApp {
val name: String
val target_platforms: Seq[BoardPlatform]
def genTop(implicit platform: Platform): HasIO[_ <: Data]
val targetPlatforms: Seq[BoardPlatform]
val cxxrtlOptions: Option[CXXRTLOptions] = None

protected val firtoolOpts = Array(
"--lowering-options=disallowLocalVariables",
"--lowering-options=disallowPackedArrays",
"-disable-all-randomization",
"-strip-debug-info",
)
def genTop(implicit platform: Platform): HasIO[_ <: Data]

def main(args: Array[String]): Unit = {
val appVersion = getClass().getPackage().getImplementationVersion()
val builder = OParser.builder[ChryseAppConfig]
val boards = target_platforms.map(_.id).mkString(",")
val boards = targetPlatforms.map(_.id).mkString(",")
val parser = {
import builder._
OParser.sequence(
programName(name),
head(
name,
appVersion,
s"(Chryse ${ChryseApp.getChrysePackage().getImplementationVersion()})",
),
help("help").text("prints this usage text"),
note(""),
cmd("build")
.action((_, c) => c.copy(mode = Some(ChryseAppModeBuild)))
.text("Build the design, and optionally program it.")
.children(
arg[String]("<board>")
.required()
.action((platform, c) => c.copy(platform = platform))
.text(s"board to build for {$boards}"),
opt[Unit]('p', "program")
.action((_, c) => c.copy(program = true))
.text("program the design onto the board after building"),
note(""),
val parsers: mutable.ArrayBuffer[OParser[Unit, ChryseAppConfig]] =
mutable.ArrayBuffer(
head(
name,
appVersion,
s"(Chryse ${ChryseApp.getChrysePackage().getImplementationVersion()})",
),
help("help").text("prints this usage text"),
note(""),
cmd("build")
.action((_, c) => c.copy(mode = Some(ChryseAppModeBuild)))
.text("Build the design, and optionally program it.")
.children(
arg[String]("<board>")
.required()
.action((platform, c) => c.copy(buildPlatform = platform))
.text(s"board to build for {$boards}"),
opt[Unit]('p', "program")
.action((_, c) => c.copy(buildProgram = true))
.text("program the design onto the board after building"),
note(""),
),
)
if (cxxrtlOptions.isDefined) {
parsers +=
cmd("cxxsim")
.action((_, c) => c.copy(mode = Some(ChryseAppModeCxxsim)))
.text("Run the C++ simulator tests.")
.children(
opt[Unit]('c', "compile")
.action((_, c) => c.copy(cxxrtlCompileOnly = true))
.text("compile only; don't run"),
opt[Unit]('O', "optimize")
.action((_, c) => c.copy(cxxrtlOptimize = true))
.text("build with optimizations"),
opt[Unit]('d', "debug")
.action((_, c) => c.copy(cxxrtlDebug = true))
.text("generate source-level debug information"),
opt[Unit]('v', "vcd")
.action((_, c) => c.copy(cxxrtlVcd = true))
.text("output a VCD file when running cxxsim (passes --vcd)"),
note(""),
)
}
parsers +=
checkConfig(c =>
if (c.mode.isEmpty) failure("no mode specified")
else success,
),
)
)
OParser.sequence(programName(name), parsers.toSeq: _*)
}

val setup = new DefaultOParserSetup {
Expand All @@ -85,98 +100,37 @@ abstract class ChryseApp {
s"(Chryse ${ChryseApp.getChrysePackage().getImplementationVersion()})",
)

val platform = target_platforms.find(_.id == config.platform).get
println(s"Building for ${platform.id} ...")

Files.createDirectories(Paths.get("build"))

val verilogPath = s"build/$name-${platform.id}.sv"
val verilog =
ChiselStage.emitSystemVerilog(
platform(genTop(platform)),
firtoolOpts = firtoolOpts,
)
writeCare(verilogPath, verilog)

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

runCare(
"synthesis",
Seq(
"yosys",
"-q",
"-g",
"-l",
s"build/$name-${platform.id}.rpt",
"-s",
yosysScriptPath,
),
)

// TODO: generate PCF.

val ascPath = s"build/$name-${platform.id}.asc"
runCare(
"place and route",
Seq(
platform.nextpnrBinary,
"-q",
"--log",
s"build/$name-${platform.id}.tim",
"--json",
jsonPath,
"--pcf",
s"$name-${platform.id}.pcf",
"--asc",
ascPath,
) ++ platform.nextpnrArgs,
)

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

if (config.program) {
runCare("program", Seq(platform.programBinary, binPath))
}
}

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

private def runCare(step: String, cmd: Seq[String]): Unit = {
val result = cmd.!
if (result != 0) {
throw new ChryseAppStepFailureException(step)
config.mode.get match {
case ChryseAppModeBuild =>
tasks.BuildTask(
name,
targetPlatforms.find(_.id == config.buildPlatform).get,
genTop(_),
config.buildProgram,
)
case ChryseAppModeCxxsim =>
tasks.CxxsimTask(name, genTop(_), cxxrtlOptions.get, config)
}
}
}

class ChryseAppStepFailureException(step: String)
extends Exception(s"Chryse step failed: $step") {}
object ChryseApp {
def getChrysePackage(): Package = this.getClass().getPackage()
}

sealed trait ChryseAppMode
case object ChryseAppModeBuild extends ChryseAppMode
final case object ChryseAppModeBuild extends ChryseAppMode
final case object ChryseAppModeCxxsim extends ChryseAppMode

case class ChryseAppConfig(
final case class ChryseAppConfig(
mode: Option[ChryseAppMode] = None,
platform: String = "",
program: Boolean = false,
buildPlatform: String = "",
buildProgram: Boolean = false,
cxxrtlCompileOnly: Boolean = false,
cxxrtlOptimize: Boolean = false,
cxxrtlDebug: Boolean = false,
cxxrtlVcd: Boolean = false,
)

object ChryseApp {
def getChrysePackage(): Package = this.getClass().getPackage()
}
class ChryseAppStepFailureException(step: String)
extends Exception(s"Chryse step failed: $step") {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package ee.hrzn.chryse.platform.cxxrtl

final case class CXXRTLOptions(clockHz: Int)
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ import chisel3._
import ee.hrzn.chryse.HasIO
import ee.hrzn.chryse.platform.ElaboratablePlatform
import ee.hrzn.chryse.platform.GenericTop
import ee.hrzn.chryse.platform.Platform

case class CXXRTLPlatform(clockHz: Int) extends ElaboratablePlatform {
val id = "cxxrtl"
final case class CXXRTLPlatform(options: CXXRTLOptions)
extends ElaboratablePlatform {
val id = "cxxrtl"
val clockHz = options.clockHz

override def apply[Top <: HasIO[_ <: Data]](top: => Top) =
GenericTop(top)(this)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,9 @@ package ee.hrzn.chryse.platform.ecp5

import chisel3._
import ee.hrzn.chryse.HasIO
import ee.hrzn.chryse.platform.ElaboratablePlatform
import ee.hrzn.chryse.platform.Platform
import ee.hrzn.chryse.platform.BoardPlatform

case object ECP5Platform extends ElaboratablePlatform with BoardPlatform {
case object ECP5Platform extends BoardPlatform {
val id = "ecp5"
val clockHz = 48_000_000

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,9 @@ package ee.hrzn.chryse.platform.ice40
import chisel3._
import ee.hrzn.chryse.HasIO
import ee.hrzn.chryse.platform.BoardPlatform
import ee.hrzn.chryse.platform.ElaboratablePlatform
import ee.hrzn.chryse.platform.Platform

case class ICE40Platform(ubtnReset: Boolean = false)
extends ElaboratablePlatform
with BoardPlatform {
final case class ICE40Platform(ubtnReset: Boolean = false)
extends BoardPlatform {
val id = "ice40"
val clockHz = 12_000_000

Expand Down
31 changes: 31 additions & 0 deletions src/main/scala/ee/hrzn/chryse/tasks/BaseTask.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package ee.hrzn.chryse.tasks

import ee.hrzn.chryse.ChryseAppStepFailureException

import java.io.PrintWriter
import scala.sys.process._

abstract class BaseTask {
protected val buildDir = "build"

protected val firtoolOpts = Array(
"--lowering-options=disallowLocalVariables",
"--lowering-options=disallowPackedArrays",
"-disable-all-randomization",
"-strip-debug-info",
)

protected def writeCare(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) {
throw new ChryseAppStepFailureException(step)
}
}
}
Loading

0 comments on commit 4d45e56

Please sign in to comment.