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

Commit

Permalink
Merge pull request #3 from chryse-hdl/zigsim
Browse files Browse the repository at this point in the history
Zigsim / ST-20
  • Loading branch information
kivikakk authored Jun 7, 2024
2 parents 02f9694 + 81361b9 commit 572c41e
Show file tree
Hide file tree
Showing 21 changed files with 637 additions and 461 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
build/
/build/

.bsp/
target/
Expand Down
31 changes: 15 additions & 16 deletions src/main/scala/ee/hrzn/chryse/ChryseApp.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@ import chisel3.Module
import ee.hrzn.chryse.platform.Platform
import ee.hrzn.chryse.platform.PlatformBoard
import ee.hrzn.chryse.platform.PlatformBoardResources
import ee.hrzn.chryse.platform.cxxrtl.CxxrtlOptions
import ee.hrzn.chryse.platform.cxxrtl.CxxrtlPlatform

abstract class ChryseApp {
val name: String
def genTop()(implicit platform: Platform): Module
val targetPlatforms: Seq[PlatformBoard[_ <: PlatformBoardResources]]
val cxxrtlPlatforms: Seq[CxxrtlPlatform] = Seq()
val additionalSubcommands: Seq[ChryseSubcommand] = Seq()
val cxxrtlOptions: Option[CxxrtlOptions] = None

def main(args: Array[String]): Unit = {
val conf = new ChryseScallopConf(this, args)
Expand Down Expand Up @@ -70,26 +70,25 @@ abstract class ChryseApp {
),
)

case Some(conf.cxxsim) =>
case Some(conf.cxxrtl) =>
println(conf.versionBanner)
val platform =
if (conf.cxxsim.platformChoices.length > 1)
conf.cxxsim.platformChoices
.find(_.id == conf.cxxsim.platform.get())
if (cxxrtlPlatforms.length > 1)
cxxrtlPlatforms
.find(_.id == conf.cxxrtl.platform.get())
.get
else
conf.cxxsim.platformChoices(0)
tasks.CxxsimTask(
cxxrtlPlatforms(0)
tasks.CxxrtlTask(
this,
platform,
cxxrtlOptions.get,
tasks.CxxsimTask.Options(
conf.cxxsim.debug(),
conf.cxxsim.optimize(),
conf.cxxsim.force(),
conf.cxxsim.compileOnly(),
conf.cxxsim.vcd.toOption,
conf.cxxsim.trailing.getOrElse(List.empty),
tasks.CxxrtlTask.Options(
conf.cxxrtl.debug(),
conf.cxxrtl.optimize(),
conf.cxxrtl.force(),
conf.cxxrtl.compileOnly(),
conf.cxxrtl.vcd.toOption,
conf.cxxrtl.trailing.getOrElse(List.empty),
),
)

Expand Down
18 changes: 8 additions & 10 deletions src/main/scala/ee/hrzn/chryse/ChryseScallopConf.scala
Original file line number Diff line number Diff line change
Expand Up @@ -90,16 +90,14 @@ private[chryse] class ChryseScallopConf(chryse: ChryseApp, args: Array[String])
}
addSubcommand(build)

object cxxsim extends Subcommand("cxxsim") {
banner("Run the C++ simulator tests.")

val platformChoices = chryse.cxxrtlOptions.map(_.platforms).getOrElse(Seq())
object cxxrtl extends Subcommand("cxxrtl") {
banner("Run the CXXRTL simulator tests.")

val platform =
if (platformChoices.length > 1)
if (chryse.cxxrtlPlatforms.length > 1)
Some(
choice(
platformChoices.map(_.id),
chryse.cxxrtlPlatforms.map(_.id),
name = "platform",
argName = "platform",
descr = "CXXRTL platform to use.",
Expand Down Expand Up @@ -128,16 +126,16 @@ private[chryse] class ChryseScallopConf(chryse: ChryseApp, args: Array[String])
val vcd =
opt[String](
argName = "file",
descr = "Output a VCD file when running cxxsim (passes --vcd <file> to the executable)",
descr = "Output a VCD file when running simulation (passes --vcd <file> to the simulation executable)",
)
val trailing = trailArg[List[String]](
name = "<arg> ...",
descr = "Other arguments for the cxxsim executable",
descr = "Other arguments for the simulation executable",
required = false,
)
}
if (chryse.cxxrtlOptions.isDefined)
addSubcommand(cxxsim)
if (chryse.cxxrtlPlatforms.nonEmpty)
addSubcommand(cxxrtl)

for { sc <- chryse.additionalSubcommands }
addSubcommand(sc)
Expand Down
7 changes: 3 additions & 4 deletions src/main/scala/ee/hrzn/chryse/ExampleApp.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ package ee.hrzn.chryse

import chisel3._
import ee.hrzn.chryse.platform.Platform
import ee.hrzn.chryse.platform.cxxrtl.CxxrtlOptions
import ee.hrzn.chryse.platform.cxxrtl.CxxrtlPlatform
import ee.hrzn.chryse.platform.ecp5.Lfe5U_85F
import ee.hrzn.chryse.platform.ecp5.Ulx3SPlatform
Expand All @@ -33,9 +32,9 @@ private[chryse] object ExampleApp extends ChryseApp {
override def genTop()(implicit platform: Platform) = new Top
override val targetPlatforms =
Seq(IceBreakerPlatform(), Ulx3SPlatform(Lfe5U_85F))
override val cxxrtlOptions = Some(
CxxrtlOptions(platforms = Seq(new CxxrtlPlatform("ex") {
override val cxxrtlPlatforms = Seq(
new CxxrtlPlatform("ex") {
val clockHz = 3_000_000
})),
},
)
}
111 changes: 111 additions & 0 deletions src/main/scala/ee/hrzn/chryse/build/CommandRunner.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/* Copyright © 2024 Asherah Connor.
*
* This file is part of Chryse.
*
* Chryse is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* Chryse is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Chryse. If not, see <https://www.gnu.org/licenses/>.
*/

package ee.hrzn.chryse.build

import ee.hrzn.chryse.ChryseAppStepFailureException

import java.io.File
import scala.sys.process.Process
import scala.util.matching.Regex

object CommandRunner {
sealed case class CmdStep(s: String) {
override def toString() = s
}
object CmdStep {
object Synthesise extends CmdStep("synthesise")
object PNR extends CmdStep("place&route")
object Pack extends CmdStep("pack")
object Program extends CmdStep("program")
object Compile extends CmdStep("compile")
object Link extends CmdStep("link")
object Execute extends CmdStep("execute")
}

private def paddedStep(step: CmdStep): String = {
val r = s"($step)"
val spaces = "(place&route) ".length() - r.length()
r + " " * spaces
}

private val specialChar = "[^a-zA-Z0-9,./=+-_:@%^]".r

// This isn't rigorous and it isn't meant to be — for displaying on stdout
// only.
private def formattedCmd(cmd: (Seq[String], Option[String])): String = {
def fmtPart(part: String) =
specialChar.replaceAllIn(part, Regex.quoteReplacement("\\") + "$0")
cmd._2.map(dir => s"(in $dir/) ").getOrElse("") +
cmd._1.map(fmtPart).mkString(" ")
}

sealed trait CmdAction
object CmdAction {
final case object Run extends CmdAction
final case object Skip extends CmdAction
}

def reportCmd(
step: CmdStep,
action: CmdAction,
cmd: (Seq[String], Option[String]),
): Unit = {
val paddedAction = action match {
case CmdAction.Run => "[run] "
case CmdAction.Skip => "[skip] "
}
println(s"${paddedStep(step)} $paddedAction ${formattedCmd(cmd)}")
}

def runCmd(step: CmdStep, cmd: Seq[String]) =
runCmds(step, Seq((cmd, None)))

def runCmds(
step: CmdStep,
cmds: Iterable[(Seq[String], Option[String])],
): Unit = {
cmds.foreach(reportCmd(step, CmdAction.Run, _))
val processes = cmds.map { cmd =>
val pb = Process(cmd._1, cmd._2.map(new File(_)))
(cmd, pb.run())
}
// TODO: consider an upper limit on concurrency.
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" ${formattedCmd(cmd)}")
throw new ChryseAppStepFailureException(step.toString())
}
}

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

def runCus(
step: CmdStep,
cus: Iterable[CompilationUnit],
): Unit = {
val (skip, run) = cus.partition(_.isUpToDate())
skip.foreach(cu => reportCmd(step, CmdAction.Skip, (cu.cmd, cu.chdir)))
runCmds(step, run.map(cu => (cu.cmd, cu.chdir)))
run.foreach(_.markUpToDate())
}
}
71 changes: 71 additions & 0 deletions src/main/scala/ee/hrzn/chryse/build/CompilationUnit.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/* Copyright © 2024 Asherah Connor.
*
* This file is part of Chryse.
*
* Chryse is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* Chryse is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Chryse. If not, see <https://www.gnu.org/licenses/>.
*/

package ee.hrzn.chryse.build

import java.nio.file.Files
import java.nio.file.Paths
import java.security.MessageDigest
import java.util.HexFormat

case class CompilationUnit(
val primaryInPath: Option[String],
val otherInPaths: Seq[String],
val outPath: String,
val cmd: Seq[String],
val chdir: Option[String] = None,
) {
val digestPath = s"$outPath.dig"
private val sortedInPaths = otherInPaths.appendedAll(primaryInPath).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(sortedInPaths.length)
for { inPath <- sortedInPaths } {
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())
}
33 changes: 33 additions & 0 deletions src/main/scala/ee/hrzn/chryse/build/filesInDirWithExt.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/* Copyright © 2024 Asherah Connor.
*
* This file is part of Chryse.
*
* Chryse is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* Chryse is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Chryse. If not, see <https://www.gnu.org/licenses/>.
*/

package ee.hrzn.chryse.build

import java.nio.file.Files
import java.nio.file.Path
import scala.jdk.CollectionConverters._

object filesInDirWithExt {
def apply(dir: String, ext: String): Iterator[String] =
Files
.walk(Path.of(dir), Integer.MAX_VALUE)
.iterator
.asScala
.map(_.toString)
.filter(_.endsWith(ext))
}
Loading

0 comments on commit 572c41e

Please sign in to comment.