Compare commits
20 Commits
9f5c9b4fc3
...
main
Author | SHA1 | Date | |
---|---|---|---|
25c93b8a73 | |||
4dcb6efcef | |||
420e50f23a | |||
6c7e7f496b | |||
44ab3f4622 | |||
2738df3bff | |||
80c9d458b4 | |||
1b450758d3 | |||
7954a9dbe3 | |||
3824dd4d29 | |||
32e626a5a1 | |||
e6f3474fd9 | |||
5218ed37b1 | |||
3d981d16ed | |||
9d0c71dedd | |||
3858761e66 | |||
be691c96be | |||
22a1f31683 | |||
47614ad47f | |||
69b63d93da |
@@ -1,3 +1,5 @@
|
|||||||
# TGC Hammer
|
# TGC Hammer
|
||||||
|
|
||||||
This is TGC-Hammer, the HLS flow for custom ISA-Extensions.
|
This is the base repo TGC-Hammer, the HLS flow for custom ISA-Extensions. It is used for building the toolflow executable, as well as the various tools (Longnail, SCAIE-V, ...).
|
||||||
|
|
||||||
|
To use it, create a new workspace folder by calling the `init-workspace.sh` script from an empty folder. This will create the base workspace structure, including a README about usage details. The workspace will always use the executables from this repo, so if you rebuild them, the new ones will automatically be used.
|
@@ -29,7 +29,7 @@ else
|
|||||||
echo "export TGC_HAMMER_WORKDIR=\"$PWD\"" >> source.sh
|
echo "export TGC_HAMMER_WORKDIR=\"$PWD\"" >> source.sh
|
||||||
echo "export PATH=\"${TGC_HAMMER_HOME}/toolflow/target/universal/stage/bin:$PATH\"" >> source.sh
|
echo "export PATH=\"${TGC_HAMMER_HOME}/toolflow/target/universal/stage/bin:$PATH\"" >> source.sh
|
||||||
|
|
||||||
echo "source source.sh" > .envrc
|
cp -r ${TGC_HAMMER_HOME}/wsTemplate/. $PWD
|
||||||
|
|
||||||
mkdir -p output
|
mkdir -p output
|
||||||
fi
|
fi
|
@@ -11,5 +11,7 @@ lazy val root = project
|
|||||||
|
|
||||||
libraryDependencies += "org.rogach" %% "scallop" % "5.2.0",
|
libraryDependencies += "org.rogach" %% "scallop" % "5.2.0",
|
||||||
libraryDependencies += "com.lihaoyi" %% "os-lib" % "0.11.5",
|
libraryDependencies += "com.lihaoyi" %% "os-lib" % "0.11.5",
|
||||||
|
libraryDependencies += "com.typesafe.scala-logging" %% "scala-logging" % "3.9.6",
|
||||||
|
libraryDependencies += "ch.qos.logback" % "logback-classic" % "1.5.19",
|
||||||
libraryDependencies += "org.scalameta" %% "munit" % "1.2.0" % Test
|
libraryDependencies += "org.scalameta" %% "munit" % "1.2.0" % Test
|
||||||
)
|
)
|
||||||
|
@@ -1,11 +0,0 @@
|
|||||||
package com.minres.tgc.hammer
|
|
||||||
|
|
||||||
import os.*
|
|
||||||
|
|
||||||
object FileUtils {
|
|
||||||
def changeExtension(path: os.Path, newExt: String): os.Path = {
|
|
||||||
val baseName = path.baseName
|
|
||||||
val newName = s"$baseName.$newExt"
|
|
||||||
path / os.up / newName
|
|
||||||
}
|
|
||||||
}
|
|
@@ -9,5 +9,8 @@ object Global {
|
|||||||
lazy val TREENAIL: Path = HAMMER / "deps" / "treenail"
|
lazy val TREENAIL: Path = HAMMER / "deps" / "treenail"
|
||||||
lazy val LONGNAIL: Path = HAMMER / "deps" / "longnail"
|
lazy val LONGNAIL: Path = HAMMER / "deps" / "longnail"
|
||||||
lazy val BASE_DIR: Path = os.pwd
|
lazy val BASE_DIR: Path = os.pwd
|
||||||
|
def OUT_DIR: Path = Main.conf.outputDirectory()
|
||||||
|
def TMP_DIR: Path = OUT_DIR / "tmp"
|
||||||
|
def LOG_DIR: Path = OUT_DIR / "logs"
|
||||||
lazy val CORE_DATASHEETS: Path = HAMMER / "coreDatasheets"
|
lazy val CORE_DATASHEETS: Path = HAMMER / "coreDatasheets"
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,10 @@
|
|||||||
package com.minres.tgc.hammer
|
package com.minres.tgc.hammer
|
||||||
|
|
||||||
|
import ch.qos.logback.classic.Level
|
||||||
import com.minres.tgc.hammer.cli.{HammerConf, MySubcommand}
|
import com.minres.tgc.hammer.cli.{HammerConf, MySubcommand}
|
||||||
|
import com.minres.tgc.hammer.util.AssertException
|
||||||
|
import com.typesafe.scalalogging.Logger
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
|
||||||
import scala.compiletime.uninitialized
|
import scala.compiletime.uninitialized
|
||||||
|
|
||||||
@@ -8,14 +12,41 @@ object Main {
|
|||||||
var conf: HammerConf = uninitialized
|
var conf: HammerConf = uninitialized
|
||||||
|
|
||||||
def main(args: Array[String]): Unit = {
|
def main(args: Array[String]): Unit = {
|
||||||
|
val logger = Logger("TGCHammer")
|
||||||
conf = new HammerConf(args.toIndexedSeq)
|
conf = new HammerConf(args.toIndexedSeq)
|
||||||
|
|
||||||
|
val logLevel = conf.verbose() match {
|
||||||
|
case 0 => Level.INFO
|
||||||
|
case 1 => Level.INFO
|
||||||
|
case 2 => Level.DEBUG
|
||||||
|
case _ => Level.TRACE
|
||||||
|
}
|
||||||
|
LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME).asInstanceOf[ch.qos.logback.classic.Logger].setLevel(logLevel)
|
||||||
|
|
||||||
|
logger.trace(s"Creating output directory ${Global.OUT_DIR}")
|
||||||
|
if (conf.emptyOutputDir()) os.remove.all(Global.OUT_DIR)
|
||||||
|
os.makeDir.all(Global.OUT_DIR)
|
||||||
|
os.makeDir.all(Global.LOG_DIR)
|
||||||
|
logger.trace(s"Creating temp directory ${Global.TMP_DIR}")
|
||||||
|
os.makeDir.all(Global.TMP_DIR)
|
||||||
conf.subcommand match {
|
conf.subcommand match {
|
||||||
case Some(c: MySubcommand) =>
|
case Some(c: MySubcommand) =>
|
||||||
|
logger.info(s"Executing subcommand ${c.name}")
|
||||||
val tasks = c.getRequiredTasks
|
val tasks = c.getRequiredTasks
|
||||||
tasks.foreach(_.validate())
|
logger.debug(s"Subcommand requires ${tasks.size} tasks")
|
||||||
tasks.foreach(_.execute())
|
try {
|
||||||
|
tasks.foreach(_.run())
|
||||||
|
c.cleanup()
|
||||||
|
} catch {
|
||||||
|
case e: AssertException =>
|
||||||
|
logger.error(s"Error during task execution, see above!")
|
||||||
|
case e: Exception =>
|
||||||
|
logger.error(s"General exception ${e.getMessage}")
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
case _ =>
|
case _ =>
|
||||||
|
logger.error(s"Found no subcommand, see help below")
|
||||||
|
conf.printHelp()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -4,7 +4,14 @@ import com.minres.tgc.hammer.Global.CORE_DATASHEETS
|
|||||||
import org.rogach.scallop.*
|
import org.rogach.scallop.*
|
||||||
|
|
||||||
trait CoreSelection { this: MySubcommand =>
|
trait CoreSelection { this: MySubcommand =>
|
||||||
val core: ScallopOption[String] = opt[String](group = mainGroup)
|
val core: ScallopOption[String] = opt[String](group = mainGroup, required = true, descr = "The core to be extended; core datasheets are in coreDatasheets/")
|
||||||
|
def getCoreDatasheet: os.Path = CORE_DATASHEETS / s"${core()}.yaml"
|
||||||
|
|
||||||
def getCoreDatasheet: os.Path = CORE_DATASHEETS / s"$core.yaml"
|
addValidation {
|
||||||
|
if (os.isFile(getCoreDatasheet)) {
|
||||||
|
Right(())
|
||||||
|
} else {
|
||||||
|
Left(s"Core ${core()} not supported, no core datasheet at ${getCoreDatasheet}")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,22 +1,28 @@
|
|||||||
package com.minres.tgc.hammer.cli
|
package com.minres.tgc.hammer.cli
|
||||||
|
|
||||||
import com.minres.tgc.hammer.Global
|
import com.minres.tgc.hammer.Global.LOG_DIR
|
||||||
import org.rogach.scallop.{ScallopConf, ScallopOption, ValueConverter, fileConverter, listArgConverter, singleArgConverter}
|
import org.rogach.scallop.*
|
||||||
import os.Path
|
import os.Path
|
||||||
|
import com.minres.tgc.hammer.util.FileUtils.*
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
class HammerConf(arguments: Seq[String]) extends ScallopConf(arguments) {
|
class HammerConf(arguments: Seq[String]) extends ScallopConf(arguments) {
|
||||||
|
val outputDirectory: ScallopOption[Path] = opt[Path](default = Some("output".path()), descr = "The base output directory")
|
||||||
|
val verbose: ScallopOption[Int] = tally(short = 'v', descr = "Controls the logging of tgc-hammer itself, using it multiple times (-vv) will print more. (0x => WARN, 1x => INFO, 2x => DEBUG, 3x => TRACE)")
|
||||||
|
val emptyOutputDir: ScallopOption[Boolean] = toggle(default = Some(true), descrYes = "Whether to empty the output directory at the start; defaults to true")
|
||||||
|
val printToolOutput: ScallopOption[Boolean] = toggle(descrYes = s"Whether to directly print all output of the underlying tools to the terminal. Otherwise the output will be written to files in `logs` folder in the selected outputDirectory", default = Some(false))
|
||||||
|
addSubcommand(new LongnailCommand)
|
||||||
addSubcommand(new TreenailCommand)
|
addSubcommand(new TreenailCommand)
|
||||||
|
addSubcommand(new LongnailMergeCommand)
|
||||||
addSubcommand(new LongnailSchedCommand)
|
addSubcommand(new LongnailSchedCommand)
|
||||||
|
|
||||||
|
banner("""This program offers several subcommands to execute different tasks.
|
||||||
|
|The main subcommand to create the hardware module of an ISAX is `isaxHLS`.
|
||||||
|
|For more information about the several subcommands use `tgc-hammer subcommand --help` with the name of the respective subcommand.
|
||||||
|
|
|
||||||
|
|Usage: tgc-hammer isaxHLS -c VexRiscv --useMinIISolution isax.core_desc
|
||||||
|
|""".stripMargin)
|
||||||
|
|
||||||
|
shortSubcommandsHelp(true)
|
||||||
|
|
||||||
verify()
|
verify()
|
||||||
}
|
}
|
||||||
|
|
||||||
implicit val osPathConverter: ValueConverter[os.Path] = {
|
|
||||||
singleArgConverter[os.Path](os.Path(_, Global.BASE_DIR))
|
|
||||||
}
|
|
||||||
|
|
||||||
implicit val osPathListConverter: ValueConverter[List[os.Path]] = {
|
|
||||||
listArgConverter[os.Path](os.Path(_, Global.BASE_DIR))
|
|
||||||
}
|
|
||||||
|
@@ -0,0 +1,53 @@
|
|||||||
|
package com.minres.tgc.hammer.cli
|
||||||
|
|
||||||
|
import com.minres.tgc.hammer.util.FileUtils.*
|
||||||
|
import com.minres.tgc.hammer.Global.*
|
||||||
|
import com.minres.tgc.hammer.tasks.{Task, TreenailTask}
|
||||||
|
import com.minres.tgc.hammer.tasks.longnail.{LongnailHLSTask, LongnailMergeTask, LongnailScheduleTask, SchedulingParameters}
|
||||||
|
import com.minres.tgc.hammer.util.{Logging, ValidationUtils}
|
||||||
|
import org.rogach.scallop.*
|
||||||
|
import os.Path
|
||||||
|
|
||||||
|
import scala.collection.mutable
|
||||||
|
|
||||||
|
class LongnailCommand extends MySubcommand("isaxHLS") with CoreSelection with Logging[LongnailCommand] with ValidationUtils {
|
||||||
|
val inputFiles: ScallopOption[List[Path]] = trailArg[List[Path]]("inputFiles", group = mainGroup, descr = "One or multiple input files; Both .core_desc as well as .mlir are supported (also mixed)")
|
||||||
|
val useMinIISolution: ScallopOption[Boolean] = toggle(name = "useMinIISolution", group = mainGroup, default = Some(false), descrYes = "Whether to automatically choose the scheduling solution with the lowest II. If not activated, a manual selection during execution will be required!")
|
||||||
|
val schedParams: SchedulingParameters = addConfigElement(new SchedulingParameters)
|
||||||
|
|
||||||
|
check(inputFiles)(checkPathsIsFile)
|
||||||
|
|
||||||
|
banner(
|
||||||
|
"""Run the complete longnail flow from CoreDSL/MLIR to receive a SystemVerilog representation of the ISAXes
|
||||||
|
|Usage: tgc-hammer isaxHLS -c VexRiscv --useMinIISolution isax.core_desc
|
||||||
|
|""".stripMargin)
|
||||||
|
|
||||||
|
override def getRequiredTasks: Seq[Task] = {
|
||||||
|
val tasks = mutable.ListBuffer[Task]()
|
||||||
|
val (coreDSLFiles, mlirFiles) = inputFiles().partition(_.ext == "core_desc")
|
||||||
|
val treenailTasks = coreDSLFiles.map(i => TreenailTask(i, TMP_DIR / s"${i.baseName}.mlir"))
|
||||||
|
tasks ++= treenailTasks
|
||||||
|
val allMlirFiles = mlirFiles ++ treenailTasks.map(_.output)
|
||||||
|
if (allMlirFiles.size == 1) {
|
||||||
|
tasks += LongnailScheduleTask(allMlirFiles.head, getCoreDatasheet, schedParams)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
val mergedInput = TMP_DIR / "merged.mlir"
|
||||||
|
val concatInput = TMP_DIR / "concat.mlir"
|
||||||
|
tasks += LongnailMergeTask(allMlirFiles, concatInput, mergedInput)
|
||||||
|
tasks += LongnailScheduleTask(mergedInput, getCoreDatasheet, schedParams)
|
||||||
|
}
|
||||||
|
val verilogOutput = OUT_DIR / "verilog"
|
||||||
|
os.makeDir.all(verilogOutput)
|
||||||
|
if (useMinIISolution()) {
|
||||||
|
tasks += LongnailHLSTask(schedParams.schedulingSolutionFile(), None, verilogOutput)
|
||||||
|
} else {
|
||||||
|
???
|
||||||
|
}
|
||||||
|
tasks.toSeq
|
||||||
|
}
|
||||||
|
|
||||||
|
override def cleanup(): Unit = {
|
||||||
|
log.info(s"Finished, verilog output in ${OUT_DIR / "verilog"}")
|
||||||
|
}
|
||||||
|
}
|
@@ -1,5 +0,0 @@
|
|||||||
package com.minres.tgc.hammer.cli
|
|
||||||
|
|
||||||
class LongnailHLSCommand {
|
|
||||||
|
|
||||||
}
|
|
@@ -1,5 +1,43 @@
|
|||||||
package com.minres.tgc.hammer.cli
|
package com.minres.tgc.hammer.cli
|
||||||
|
|
||||||
class LongnailMergeCommand {
|
import com.minres.tgc.hammer.tasks.{CopyTask, Task, TreenailTask}
|
||||||
|
import org.rogach.scallop.*
|
||||||
|
import os.Path
|
||||||
|
import com.minres.tgc.hammer.util.FileUtils.*
|
||||||
|
import com.minres.tgc.hammer.Global.*
|
||||||
|
import com.minres.tgc.hammer.tasks.longnail.LongnailMergeTask
|
||||||
|
import com.minres.tgc.hammer.util.{Logging, ValidationUtils}
|
||||||
|
|
||||||
|
class LongnailMergeCommand extends MySubcommand("mergeISAX") with Logging[LongnailMergeCommand] with ValidationUtils {
|
||||||
|
val inputFiles: ScallopOption[List[Path]] = trailArg[List[Path]]("inputFiles", group = mainGroup, descr = "One or multiple input files; Both .core_desc as well as .mlir are supported (also mixed)")
|
||||||
|
val output: ScallopOption[Path] = opt[Path](short = 'o', default = Some("merged.mlir".path()), descr = "The .mlir file containing the merged ISAXes")
|
||||||
|
val overwrite: ScallopOption[Boolean] = toggle()
|
||||||
|
|
||||||
|
check(inputFiles)(checkPathsIsFile)
|
||||||
|
checkNotIf(overwrite)(output)(checkPathDoesNotExist)
|
||||||
|
|
||||||
|
override def getRequiredTasks: Seq[Task] = {
|
||||||
|
val (coreDSLFiles, mlirFiles) = inputFiles().partition(_.ext == "core_desc")
|
||||||
|
|
||||||
|
if (inputFiles().size == 1) {
|
||||||
|
log.debug(s"Only one input file, no merge required...")
|
||||||
|
if (coreDSLFiles.size == 1) {
|
||||||
|
log.trace(s"CoreDSL input file, creating translation task to MLIR...")
|
||||||
|
Seq(TreenailTask(coreDSLFiles.head, output()))
|
||||||
|
} else {
|
||||||
|
log.trace(s"MLIR input file, creating copy task...")
|
||||||
|
Seq(CopyTask(mlirFiles.head, output()))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.debug(s"Multiple input files, creating translation tasks to MLIR and merge task...")
|
||||||
|
val treenailTasks = coreDSLFiles.map(i => TreenailTask(i, TMP_DIR / s"${i.baseName}.mlir"))
|
||||||
|
val allMlirFiles = mlirFiles ++ treenailTasks.map(_.output)
|
||||||
|
val concatInput = TMP_DIR / "concat.mlir"
|
||||||
|
treenailTasks :+ LongnailMergeTask(allMlirFiles, concatInput, output())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override def cleanup(): Unit = {
|
||||||
|
log.info(s"Finished, merged MLIR output in ${output()}")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,29 +1,43 @@
|
|||||||
package com.minres.tgc.hammer.cli
|
package com.minres.tgc.hammer.cli
|
||||||
|
|
||||||
import com.minres.tgc.hammer.FileUtils.changeExtension
|
import com.minres.tgc.hammer.util.FileUtils.*
|
||||||
|
import com.minres.tgc.hammer.Global.*
|
||||||
import com.minres.tgc.hammer.tasks.longnail.{LongnailMergeTask, LongnailScheduleTask}
|
import com.minres.tgc.hammer.tasks.longnail.{LongnailMergeTask, LongnailScheduleTask}
|
||||||
import com.minres.tgc.hammer.tasks.{Task, TreenailTask}
|
import com.minres.tgc.hammer.tasks.{Task, TreenailTask}
|
||||||
import com.minres.tgc.hammer.tasks.longnail.SchedulingParameters
|
import com.minres.tgc.hammer.tasks.longnail.SchedulingParameters
|
||||||
import org.rogach.scallop.ScallopOption
|
import com.minres.tgc.hammer.util.{Logging, ValidationUtils}
|
||||||
|
import org.rogach.scallop.*
|
||||||
import os.Path
|
import os.Path
|
||||||
|
|
||||||
class LongnailSchedCommand extends MySubcommand("scheduleISAX") with CoreSelection {
|
class LongnailSchedCommand extends MySubcommand("scheduleISAX") with CoreSelection with Logging[LongnailSchedCommand] with ValidationUtils {
|
||||||
val inputFiles: ScallopOption[List[Path]] = trailArg[List[Path]]("inputFiles", group = mainGroup)
|
val inputFiles: ScallopOption[List[Path]] = trailArg[List[Path]]("inputFiles", group = mainGroup, descr = "One or multiple input files; Both .core_desc as well as .mlir are supported (also mixed)")
|
||||||
val outputDirectory: ScallopOption[Path] = opt[Path](group = mainGroup)
|
|
||||||
val schedParams: SchedulingParameters = addConfigElement(new SchedulingParameters)
|
val schedParams: SchedulingParameters = addConfigElement(new SchedulingParameters)
|
||||||
|
|
||||||
validateOSPathIsDirectory(outputDirectory)
|
check(inputFiles)(checkPathsIsFile)
|
||||||
|
|
||||||
|
banner(
|
||||||
|
"""Generate Scheduling information for the provided ISAXes using Longnail
|
||||||
|
|Usage: tgc-hammer scheduleISAX -c VexRiscv isax.core_desc
|
||||||
|
|""".stripMargin)
|
||||||
|
|
||||||
override def getRequiredTasks: Seq[Task] = {
|
override def getRequiredTasks: Seq[Task] = {
|
||||||
|
log.debug(s"Creating translation task to MLIR for CoreDSL inputs...")
|
||||||
val (coreDSLFiles, mlirFiles) = inputFiles().partition(_.ext == "core_desc")
|
val (coreDSLFiles, mlirFiles) = inputFiles().partition(_.ext == "core_desc")
|
||||||
val treenailTasks = coreDSLFiles.map(i => TreenailTask(i, changeExtension(i, "mlir")))
|
val treenailTasks = coreDSLFiles.map(i => TreenailTask(i, TMP_DIR / s"${i.baseName}.mlir"))
|
||||||
val allMlirFiles = mlirFiles ++ treenailTasks.map(_.output)
|
val allMlirFiles = mlirFiles ++ treenailTasks.map(_.output)
|
||||||
if (allMlirFiles.size == 1) {
|
if (allMlirFiles.size == 1) {
|
||||||
|
log.debug(s"Single input file, only creating schedule task...")
|
||||||
treenailTasks :+ LongnailScheduleTask(allMlirFiles.head, getCoreDatasheet, schedParams)
|
treenailTasks :+ LongnailScheduleTask(allMlirFiles.head, getCoreDatasheet, schedParams)
|
||||||
} else {
|
} else {
|
||||||
val mergedInput = outputDirectory() / "merged.mlir"
|
val mergedInput = TMP_DIR / "merged.mlir"
|
||||||
val concatInput = outputDirectory() / "concat.mlir"
|
val concatInput = TMP_DIR / "concat.mlir"
|
||||||
|
log.debug(s"Multiple input files, creating merge and schedule task...")
|
||||||
treenailTasks :+ LongnailMergeTask(allMlirFiles, concatInput, mergedInput) :+ LongnailScheduleTask(mergedInput, getCoreDatasheet, schedParams)
|
treenailTasks :+ LongnailMergeTask(allMlirFiles, concatInput, mergedInput) :+ LongnailScheduleTask(mergedInput, getCoreDatasheet, schedParams)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override def cleanup(): Unit = {
|
||||||
|
log.info(s"Finished, scheduling solutions in ${schedParams.schedulingSolutionFile()}")
|
||||||
|
log.info(s"Scheduling selection file (KConf) in ${schedParams.schedule.schedulingSelectionFile()}")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,27 +1,30 @@
|
|||||||
package com.minres.tgc.hammer.cli
|
package com.minres.tgc.hammer.cli
|
||||||
|
|
||||||
import com.minres.tgc.hammer.options.{BaseOption, ConfigElement, OptionGroup}
|
import com.minres.tgc.hammer.options.ConfigElement
|
||||||
import com.minres.tgc.hammer.tasks.Task
|
import com.minres.tgc.hammer.tasks.Task
|
||||||
import org.rogach.scallop.{ScallopOption, ScallopOptionGroup, Subcommand, Util}
|
import org.rogach.scallop.{ScallopOption, ScallopOptionGroup, Subcommand, Util}
|
||||||
|
import com.minres.tgc.hammer.util.{Checker, ValidationBase}
|
||||||
|
|
||||||
import java.nio.file.{Files, Path}
|
abstract class MySubcommand(val name: String) extends Subcommand(name) with ValidationBase[ScallopOption] {
|
||||||
|
|
||||||
abstract class MySubcommand(name: String) extends Subcommand(name) {
|
|
||||||
protected val mainGroup: ScallopOptionGroup = group()
|
protected val mainGroup: ScallopOptionGroup = group()
|
||||||
|
|
||||||
def getRequiredTasks: Seq[Task]
|
def getRequiredTasks: Seq[Task]
|
||||||
|
def cleanup(): Unit
|
||||||
|
|
||||||
def addConfigElement[T <: ConfigElement](elem: T): T = {
|
def addConfigElement[T <: ConfigElement](elem: T): T = {
|
||||||
elem.init(this, null)
|
elem.init(this, null)
|
||||||
elem
|
elem
|
||||||
}
|
}
|
||||||
|
|
||||||
def validateOSPathIsDirectory(pathOption: ScallopOption[os.Path]): Unit = addValidation {
|
|
||||||
pathOption.toOption
|
def check[T](option: ScallopOption[T])(check: Checker[T]): Unit = {
|
||||||
.map {
|
addValidation(check(option))
|
||||||
case path if Files.isDirectory(path.toNIO) => Right(())
|
}
|
||||||
case path => Left(Util.format("File '%s' is not a directory", path))
|
|
||||||
|
def checkPred[T, U](condOpt: ScallopOption[U], cond: U => Boolean)(option: ScallopOption[T])(check: Checker[T]): Unit = addValidation {
|
||||||
|
condOpt.toOption match {
|
||||||
|
case Some(value) if cond(value) => check(option)
|
||||||
|
case _ => Right(())
|
||||||
}
|
}
|
||||||
.getOrElse(Right(()))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,21 +1,32 @@
|
|||||||
package com.minres.tgc.hammer.cli
|
package com.minres.tgc.hammer.cli
|
||||||
|
|
||||||
import com.minres.tgc.hammer.Global
|
import com.minres.tgc.hammer.util.FileUtils.*
|
||||||
import com.minres.tgc.hammer.tasks.{Task, TreenailTask}
|
import com.minres.tgc.hammer.tasks.{Task, TreenailTask}
|
||||||
|
import com.minres.tgc.hammer.util.{Logging, ValidationUtils}
|
||||||
import org.rogach.scallop.*
|
import org.rogach.scallop.*
|
||||||
|
|
||||||
import java.io.File
|
|
||||||
import os.Path
|
import os.Path
|
||||||
|
|
||||||
class TreenailCommand extends MySubcommand("parseCoreDSL") {
|
class TreenailCommand extends MySubcommand("parseCoreDSL") with Logging[TreenailCommand] with ValidationUtils {
|
||||||
val coreDSL: ScallopOption[File] = trailArg[File]("coreDSL")
|
val coreDSL: ScallopOption[Path] = trailArg[Path]("coreDSL", descr = "The .core_desc input file to be converted")
|
||||||
val output: ScallopOption[Path] = opt[Path](short = 'o', default = Some(os.Path("isax.mlir", Global.BASE_DIR)))
|
val output: ScallopOption[Path] = opt[Path](short = 'o', default = Some("isax.mlir".path()), descr = "The .mlir file containing the converted ISAX")
|
||||||
|
val overwrite: ScallopOption[Boolean] = toggle()
|
||||||
|
|
||||||
validateFileIsFile(coreDSL)
|
check(coreDSL)(checkPathIsFile)
|
||||||
|
check(coreDSL)(checkPathIsCoreDSL)
|
||||||
|
checkNotIf(overwrite)(output)(checkPathDoesNotExist)
|
||||||
|
|
||||||
|
banner(
|
||||||
|
"""Parse a CoreDSL input using Treenail into the MLIR representation
|
||||||
|
|Usage: tgc-hammer parseCoreDSL -o isax.mlir isax.core_desc
|
||||||
|
|""".stripMargin)
|
||||||
|
|
||||||
override def getRequiredTasks: Seq[Task] = {
|
override def getRequiredTasks: Seq[Task] = {
|
||||||
Seq(
|
Seq(
|
||||||
new TreenailTask(Path(coreDSL(), os.pwd), Path(output(), os.pwd))
|
TreenailTask(coreDSL(), output())
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override def cleanup(): Unit = {
|
||||||
|
log.info(s"Finished, MLIR output in ${output()}")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -7,16 +7,20 @@ import scala.compiletime.uninitialized
|
|||||||
|
|
||||||
trait BaseOption[T](using conv: ValueConverter[T]) extends ConfigElement {
|
trait BaseOption[T](using conv: ValueConverter[T]) extends ConfigElement {
|
||||||
protected def createScallop(conf: ScallopConf, group: ScallopOptionGroup): ScallopOption[T]
|
protected def createScallop(conf: ScallopConf, group: ScallopOptionGroup): ScallopOption[T]
|
||||||
private var scallop: ScallopOption[T] = uninitialized
|
|
||||||
|
protected[options] var scallop: ScallopOption[T] = uninitialized
|
||||||
|
|
||||||
def init(scallopConf: ScallopConf, group: ScallopOptionGroup): Unit = {
|
def init(scallopConf: ScallopConf, group: ScallopOptionGroup): Unit = {
|
||||||
scallop = createScallop(scallopConf, group)
|
scallop = createScallop(scallopConf, group)
|
||||||
}
|
}
|
||||||
|
|
||||||
def get: T = scallop()
|
def default: () => Option[T]
|
||||||
def apply: T = get
|
|
||||||
|
|
||||||
def getToolParameters: Seq[Shellable] = if (scallop.isDefined) Seq(s"$toolName", getToolArg) else Seq()
|
def get: Option[T] = scallop.toOption.orElse(default())
|
||||||
|
|
||||||
|
def apply(): T = get.get
|
||||||
|
|
||||||
|
def getToolParameters: Seq[Shellable] = if (get.isDefined) Seq(s"$toolName", get.get.toString) else Seq()
|
||||||
|
|
||||||
def toolName: String
|
def toolName: String
|
||||||
def getToolArg: String = get.toString
|
|
||||||
}
|
}
|
@@ -5,8 +5,8 @@ import org.rogach.scallop.*
|
|||||||
enum Color:
|
enum Color:
|
||||||
case Red, Green, Blue
|
case Red, Green, Blue
|
||||||
|
|
||||||
class ChoiceOption(choices: Seq[String], cliName: String, val toolName: String, descr: String, default: => Option[String], required: Boolean, cliShort: Char) extends BaseOption[String] {
|
class ChoiceOption(choices: Seq[String], cliName: String, val toolName: String, descr: String, val default: () => Option[String], required: Boolean, cliShort: Char) extends BaseOption[String] {
|
||||||
override protected def createScallop(conf: ScallopConf, group: ScallopOptionGroup): ScallopOption[String] = {
|
override protected def createScallop(conf: ScallopConf, group: ScallopOptionGroup): ScallopOption[String] = {
|
||||||
conf.choice(choices, name = cliName, short = cliShort, descr = descr, default = default, required = required, group = group)
|
conf.choice(choices, name = cliName, short = cliShort, descr = descr, required = required, group = group)
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -5,12 +5,12 @@ import os.Shellable
|
|||||||
|
|
||||||
class CommandGroup(name: String) extends BaseGroup {
|
class CommandGroup(name: String) extends BaseGroup {
|
||||||
override def getToolParameters: Seq[Shellable] = {
|
override def getToolParameters: Seq[Shellable] = {
|
||||||
val sub = options.toSeq.map(_.getToolParameters.flatMap(_.value).mkString("=")).filter(!_.isEmpty)
|
val sub = options.toSeq.map(_.getToolParameters.flatMap(_.value).mkString("=")).filter(_.nonEmpty)
|
||||||
println(sub)
|
|
||||||
Seq(s"$name=\'${sub.mkString(" ")}\'")
|
Seq(s"$name=\'${sub.mkString(" ")}\'")
|
||||||
}
|
}
|
||||||
|
|
||||||
override def init(scallopConf: ScallopConf, group: ScallopOptionGroup): Unit = {
|
override def init(scallopConf: ScallopConf, group: ScallopOptionGroup): Unit = {
|
||||||
options.foreach(_.init(scallopConf, group))
|
options.foreach(_.init(scallopConf, group))
|
||||||
|
initValidation(scallopConf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,33 +1,52 @@
|
|||||||
package com.minres.tgc.hammer.options
|
package com.minres.tgc.hammer.options
|
||||||
|
|
||||||
|
|
||||||
|
import com.minres.tgc.hammer.util.{Checker, ValidationBase, ValidationUtils}
|
||||||
import org.rogach.scallop.{ScallopConf, ScallopOptionGroup, ValueConverter}
|
import org.rogach.scallop.{ScallopConf, ScallopOptionGroup, ValueConverter}
|
||||||
import os.Shellable
|
import os.Shellable
|
||||||
|
|
||||||
import scala.collection.mutable
|
import scala.collection.mutable
|
||||||
|
|
||||||
export com.minres.tgc.hammer.cli.osPathConverter
|
trait BaseGroup extends ConfigElement with ValidationBase[BaseOption] with ValidationUtils {
|
||||||
|
|
||||||
trait BaseGroup extends ConfigElement {
|
|
||||||
protected val options = mutable.ListBuffer[ConfigElement]()
|
protected val options = mutable.ListBuffer[ConfigElement]()
|
||||||
protected def add[T <: ConfigElement](option: T): T = {
|
protected def add[T <: ConfigElement](option: T): T = {
|
||||||
options += option
|
options += option
|
||||||
option
|
option
|
||||||
}
|
}
|
||||||
|
|
||||||
def value[T](cliName: String, toolName: String, descr: String = "", default: => Option[T] = None, required: Boolean = false, cliShort: Char = '\u0000')(using conv: ValueConverter[T]): ValueOption[T] = add(ValueOption(cliName, toolName, descr, default, required, cliShort))
|
def initValidation(scallopConf: ScallopConf): Unit = {
|
||||||
def valueS[T](name: String, descr: String = "", default: => Option[T] = None, required: Boolean = false, cliShort: Char = '\u0000')(using conv: ValueConverter[T]): ValueOption[T] = add(ValueOption(name, name, descr, default, required, cliShort))
|
validations.foreach(fn => scallopConf.addValidation(fn()))
|
||||||
|
}
|
||||||
|
|
||||||
def trail[T](toolName: String, descr: String = "", default: => Option[T] = None, required: Boolean = false)(using conv: ValueConverter[T]): TrailOption[T] = add(TrailOption(toolName, descr, default, required))
|
private var validations: List[() => Either[String, Unit]] = Nil
|
||||||
|
|
||||||
def choice(choices: Seq[String], cliName: String, toolName: String, descr: String = "", default: => Option[String] = None, required: Boolean = false, cliShort: Char = '\u0000'): ChoiceOption = add(ChoiceOption(choices, cliName, toolName, descr, default, required, cliShort))
|
private def addValidation(fn: () => Either[String, Unit]): Unit = {
|
||||||
def choiceS(choices: Seq[String], name: String, descr: String = "", default: => Option[String] = None, required: Boolean = false, cliShort: Char = '\u0000'): ChoiceOption = add(ChoiceOption(choices, name, name, descr, default, required, cliShort))
|
validations :+= fn
|
||||||
|
}
|
||||||
|
|
||||||
|
def check[T](option: BaseOption[T])(check: Checker[T]): Unit = {
|
||||||
|
addValidation(() => check(option.scallop))
|
||||||
|
}
|
||||||
|
def checkPred[T, U](condOpt: BaseOption[U], cond: U => Boolean)(option: BaseOption[T])(check: Checker[T]): Unit = addValidation { () =>
|
||||||
|
condOpt.scallop.toOption match {
|
||||||
|
case Some(value) if cond(value) => check(option.scallop)
|
||||||
|
case _ => Right(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def value[T](cliName: String, toolName: String, descr: String = "", default: () => Option[T] = () => None, required: Boolean = false, cliShort: Char = '\u0000')(using conv: ValueConverter[T]): ValueOption[T] = add(ValueOption(cliName, toolName, descr, default, required, cliShort))
|
||||||
|
def valueS[T](name: String, descr: String = "", default: () => Option[T] = () => None, required: Boolean = false, cliShort: Char = '\u0000')(using conv: ValueConverter[T]): ValueOption[T] = add(ValueOption(name, name, descr, default, required, cliShort))
|
||||||
|
|
||||||
|
def trail[T](toolName: String, descr: String = "", default: () => Option[T] = () => None, required: Boolean = false)(using conv: ValueConverter[T]): TrailOption[T] = add(TrailOption(toolName, descr, default, required))
|
||||||
|
|
||||||
|
def choice(choices: Seq[String], cliName: String, toolName: String, descr: String = "", default: () => Option[String] = () => None, required: Boolean = false, cliShort: Char = '\u0000'): ChoiceOption = add(ChoiceOption(choices, cliName, toolName, descr, default, required, cliShort))
|
||||||
|
def choiceS(choices: Seq[String], name: String, descr: String = "", default: () => Option[String] = () => None, required: Boolean = false, cliShort: Char = '\u0000'): ChoiceOption = add(ChoiceOption(choices, name, name, descr, default, required, cliShort))
|
||||||
|
|
||||||
def tally(cliName: String, toolName: String, descr: String = "", required: Boolean = false, cliShort: Char = '\u0000'): TallyOption = add(TallyOption(cliName, toolName, descr, required, cliShort))
|
def tally(cliName: String, toolName: String, descr: String = "", required: Boolean = false, cliShort: Char = '\u0000'): TallyOption = add(TallyOption(cliName, toolName, descr, required, cliShort))
|
||||||
def tallyS(name: String, descr: String = "", required: Boolean = false, cliShort: Char = '\u0000'): TallyOption = add(TallyOption(name, name, descr, required, cliShort))
|
def tallyS(name: String, descr: String = "", required: Boolean = false, cliShort: Char = '\u0000'): TallyOption = add(TallyOption(name, name, descr, required, cliShort))
|
||||||
|
|
||||||
def toggle(cliName: String, toolName: String, descr: String = "", default: => Option[Boolean] = None, required: Boolean = false, cliShort: Char = '\u0000'): ToggleOption = add(ToggleOption(cliName, toolName, descr, default, required, cliShort))
|
def toggle(cliName: String, toolName: String, descr: String = "", default: () => Option[Boolean] = () => None, required: Boolean = false, cliShort: Char = '\u0000'): ToggleOption = add(ToggleOption(cliName, toolName, descr, default, required, cliShort))
|
||||||
def toggleS(name: String, descr: String = "", default: => Option[Boolean] = None, required: Boolean = false, cliShort: Char = '\u0000'): ToggleOption = add(ToggleOption(name, name, descr, default, required, cliShort))
|
def toggleS(name: String, descr: String = "", default: () => Option[Boolean] = () => None, required: Boolean = false, cliShort: Char = '\u0000'): ToggleOption = add(ToggleOption(name, name, descr, default, required, cliShort))
|
||||||
}
|
}
|
||||||
|
|
||||||
trait OptionGroup extends BaseGroup {
|
trait OptionGroup extends BaseGroup {
|
||||||
@@ -38,5 +57,6 @@ trait OptionGroup extends BaseGroup {
|
|||||||
def init(scallopConf: ScallopConf, g: ScallopOptionGroup): Unit = {
|
def init(scallopConf: ScallopConf, g: ScallopOptionGroup): Unit = {
|
||||||
val group = scallopConf.group(name)
|
val group = scallopConf.group(name)
|
||||||
options.foreach(_.init(scallopConf, group))
|
options.foreach(_.init(scallopConf, group))
|
||||||
|
initValidation(scallopConf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -6,4 +6,6 @@ class TallyOption(cliName: String, val toolName: String, descr: String, required
|
|||||||
override protected def createScallop(conf: ScallopConf, group: ScallopOptionGroup): ScallopOption[Int] = {
|
override protected def createScallop(conf: ScallopConf, group: ScallopOptionGroup): ScallopOption[Int] = {
|
||||||
conf.tally(name = cliName, short = cliShort, descr = descr, group = group)
|
conf.tally(name = cliName, short = cliShort, descr = descr, group = group)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override def default: () => Option[Int] = () => None
|
||||||
}
|
}
|
@@ -2,9 +2,9 @@ package com.minres.tgc.hammer.options
|
|||||||
|
|
||||||
import org.rogach.scallop.*
|
import org.rogach.scallop.*
|
||||||
|
|
||||||
class ToggleOption(cliName: String, val toolName: String, descr: String, default: => Option[Boolean], required: Boolean, cliShort: Char) extends BaseOption[Boolean] {
|
class ToggleOption(cliName: String, val toolName: String, descr: String, val default: () => Option[Boolean], required: Boolean, cliShort: Char) extends BaseOption[Boolean] {
|
||||||
|
|
||||||
override protected def createScallop(conf: ScallopConf, group: ScallopOptionGroup): ScallopOption[Boolean] = {
|
override protected def createScallop(conf: ScallopConf, group: ScallopOptionGroup): ScallopOption[Boolean] = {
|
||||||
conf.toggle(name = cliName, short = cliShort, descrYes = descr, default = default, required = required, group = group)
|
conf.toggle(name = cliName, short = cliShort, descrYes = descr, required = required, group = group)
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -2,8 +2,8 @@ package com.minres.tgc.hammer.options
|
|||||||
|
|
||||||
import org.rogach.scallop.*
|
import org.rogach.scallop.*
|
||||||
|
|
||||||
class TrailOption[T](val toolName: String, descr: String, default: => Option[T], required: Boolean)(using conv: ValueConverter[T]) extends BaseOption[T] {
|
class TrailOption[T](val toolName: String, descr: String, val default: () => Option[T], required: Boolean)(using conv: ValueConverter[T]) extends BaseOption[T] {
|
||||||
override protected def createScallop(conf: ScallopConf, group: ScallopOptionGroup): ScallopOption[T] = {
|
override protected def createScallop(conf: ScallopConf, group: ScallopOptionGroup): ScallopOption[T] = {
|
||||||
conf.trailArg[T](descr = descr, default = default, required = required, group = group)
|
conf.trailArg[T](descr = descr, required = required, group = group)
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -2,8 +2,8 @@ package com.minres.tgc.hammer.options
|
|||||||
|
|
||||||
import org.rogach.scallop.*
|
import org.rogach.scallop.*
|
||||||
|
|
||||||
class ValueOption[T](cliName: String, val toolName: String, descr: String, default: => Option[T], required: Boolean, cliShort: Char)(using conv: ValueConverter[T]) extends BaseOption[T] {
|
class ValueOption[T](cliName: String, val toolName: String, descr: String, val default: () => Option[T], required: Boolean, cliShort: Char)(using conv: ValueConverter[T]) extends BaseOption[T] {
|
||||||
override protected def createScallop(conf: ScallopConf, group: ScallopOptionGroup): ScallopOption[T] = {
|
override protected def createScallop(conf: ScallopConf, group: ScallopOptionGroup): ScallopOption[T] = {
|
||||||
conf.opt[T](name = cliName, short = cliShort, descr = descr, default = default, required = required, group = group)
|
conf.opt[T](name = cliName, short = cliShort, descr = descr, required = required, group = group)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,13 @@
|
|||||||
|
package com.minres.tgc.hammer.tasks
|
||||||
|
|
||||||
|
import os.*
|
||||||
|
|
||||||
|
case class CopyTask(from: Path, to: Path) extends TaskImpl[CopyTask] {
|
||||||
|
override def validate(): Unit = {
|
||||||
|
assert(isFile(from), "Input file does not exist")
|
||||||
|
}
|
||||||
|
|
||||||
|
override def execute(): Unit = {
|
||||||
|
copy(from, to)
|
||||||
|
}
|
||||||
|
}
|
@@ -1,16 +1,32 @@
|
|||||||
package com.minres.tgc.hammer.tasks
|
package com.minres.tgc.hammer.tasks
|
||||||
|
|
||||||
|
import com.minres.tgc.hammer.Global.TMP_DIR
|
||||||
|
import com.minres.tgc.hammer.Main
|
||||||
|
import com.minres.tgc.hammer.util.Logging
|
||||||
import os.{Path, Shellable}
|
import os.{Path, Shellable}
|
||||||
|
|
||||||
|
import scala.reflect.ClassTag
|
||||||
|
|
||||||
trait Task {
|
trait Task {
|
||||||
def validate(): Unit
|
def validate(): Unit
|
||||||
def execute(): Unit
|
def execute(): Unit
|
||||||
|
|
||||||
def runExecutable(execPath: Path, args: Shellable*): os.CommandResult = {
|
def run(): Unit = {
|
||||||
println(s"Executing $execPath with")
|
validate()
|
||||||
println(args.flatMap(_.value).mkString(" "))
|
execute()
|
||||||
println(args)
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait TaskImpl[T <: Task : ClassTag] extends Task with Logging[T] {
|
||||||
|
|
||||||
|
def runExecutable(execPath: Path, args: Shellable*)(logFile: os.Path): os.CommandResult = {
|
||||||
val command = s"$execPath ${args.flatMap(_.value).mkString(" ")}"
|
val command = s"$execPath ${args.flatMap(_.value).mkString(" ")}"
|
||||||
os.proc("bash", "-c", command).call(stdout = os.Inherit)
|
log.debug(s"Running external program: ")
|
||||||
|
log.debug(command)
|
||||||
|
val output: os.ProcessOutput = if (Main.conf.printToolOutput())
|
||||||
|
os.Inherit
|
||||||
|
else
|
||||||
|
logFile
|
||||||
|
os.proc("bash", "-c", command).call(cwd = TMP_DIR, stdout = output)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,16 +1,17 @@
|
|||||||
package com.minres.tgc.hammer.tasks
|
package com.minres.tgc.hammer.tasks
|
||||||
|
|
||||||
import com.minres.tgc.hammer.Global
|
import com.minres.tgc.hammer.Global.*
|
||||||
|
import com.minres.tgc.hammer.util.FileUtils.*
|
||||||
import os.*
|
import os.*
|
||||||
|
|
||||||
case class TreenailTask(coreDSLInput: Path, output: Path) extends Task {
|
case class TreenailTask(coreDSLInput: Path, output: Path) extends TaskImpl[TreenailTask] {
|
||||||
private val EXECUTABLE = Global.TREENAIL / "app" / "build" / "install" / "app" / "bin" / "app"
|
private val EXECUTABLE = TREENAIL / "app" / "build" / "install" / "app" / "bin" / "app"
|
||||||
|
|
||||||
override def validate(): Unit = {
|
override def validate(): Unit = {
|
||||||
assert(isFile(EXECUTABLE), "Treenail Executable is missing, build Treenail")
|
assert(isFile(EXECUTABLE), "Treenail Executable is missing, build Treenail")
|
||||||
|
assert(isCoreDSLFile(coreDSLInput), "Input file does not exist")
|
||||||
}
|
}
|
||||||
override def execute(): Unit = {
|
override def execute(): Unit = {
|
||||||
runExecutable(EXECUTABLE, "-o", output, coreDSLInput)
|
runExecutable(EXECUTABLE, "-o", output, coreDSLInput)(LOG_DIR / "treenail.log")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -2,10 +2,10 @@ package com.minres.tgc.hammer.tasks.longnail
|
|||||||
|
|
||||||
import com.minres.tgc.hammer.Global
|
import com.minres.tgc.hammer.Global
|
||||||
import com.minres.tgc.hammer.options.*
|
import com.minres.tgc.hammer.options.*
|
||||||
import com.minres.tgc.hammer.tasks.Task
|
import com.minres.tgc.hammer.tasks.{Task, TaskImpl}
|
||||||
import os.{Path, isFile}
|
import os.{Path, isFile}
|
||||||
|
|
||||||
trait LongnailBaseTask extends Task {
|
trait LongnailBaseTask[T <: LongnailBaseTask[T]] extends TaskImpl[T] {
|
||||||
protected val EXECUTABLE: os.Path = Global.LONGNAIL / "build" / "bin" / "longnail-opt"
|
protected val EXECUTABLE: os.Path = Global.LONGNAIL / "build" / "bin" / "longnail-opt"
|
||||||
|
|
||||||
override def validate(): Unit = {
|
override def validate(): Unit = {
|
||||||
|
@@ -1,18 +1,22 @@
|
|||||||
package com.minres.tgc.hammer.tasks.longnail
|
package com.minres.tgc.hammer.tasks.longnail
|
||||||
|
|
||||||
import os.Path
|
import com.minres.tgc.hammer.Global.LOG_DIR
|
||||||
|
import com.minres.tgc.hammer.util.FileUtils.*
|
||||||
|
import os.*
|
||||||
|
|
||||||
class LongnailHLSTask(schedulingFile: Option[Path], outDirectory: Path) extends LongnailBaseTask {
|
class LongnailHLSTask(schedulingSolutionFile: Path, schedulingSelectionFile: Option[Path], outDirectory: Path) extends LongnailBaseTask[LongnailHLSTask] {
|
||||||
override def validate(): Unit = {
|
override def validate(): Unit = {
|
||||||
super.validate()
|
super.validate()
|
||||||
|
assert(isMLIRFile(schedulingSolutionFile), "Scheduling solution file does not exist")
|
||||||
|
schedulingSelectionFile.foreach(f => assert(isFile(f), "Scheduling selection file does not exist"))
|
||||||
|
assert(isDir(outDirectory), "Output directory does not exist")
|
||||||
}
|
}
|
||||||
|
|
||||||
override def execute(): Unit = {
|
override def execute(): Unit = {
|
||||||
runExecutable(EXECUTABLE,
|
runExecutable(EXECUTABLE,
|
||||||
"--lower-lil-to-hw",
|
schedulingSelectionFile match {
|
||||||
schedulingFile match {
|
case Some(value) => s"--lower-lil-to-hw=solutionSelection=$value"
|
||||||
case Some(value) => s"--solutionSelection $value"
|
case None => "--lower-lil-to-hw=forceUseMinIISolution=true"
|
||||||
case None => "--forceUseMinIISolution=true"
|
|
||||||
},
|
},
|
||||||
"--simplify-structure",
|
"--simplify-structure",
|
||||||
"--cse",
|
"--cse",
|
||||||
@@ -22,8 +26,8 @@ class LongnailHLSTask(schedulingFile: Option[Path], outDirectory: Path) extends
|
|||||||
"--hw-cleanup",
|
"--hw-cleanup",
|
||||||
"--prettify-verilog",
|
"--prettify-verilog",
|
||||||
"--hw-legalize-modules",
|
"--hw-legalize-modules",
|
||||||
"--export-split-verilog",
|
s"--export-split-verilog=dir-name=$outDirectory",
|
||||||
"--dir-name", outDirectory
|
schedulingSolutionFile
|
||||||
)
|
)(LOG_DIR / "longnail_hls.log")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,10 +1,13 @@
|
|||||||
package com.minres.tgc.hammer.tasks.longnail
|
package com.minres.tgc.hammer.tasks.longnail
|
||||||
|
|
||||||
import os.Path
|
import com.minres.tgc.hammer.Global.LOG_DIR
|
||||||
|
import com.minres.tgc.hammer.util.FileUtils.*
|
||||||
|
import os.*
|
||||||
|
|
||||||
class LongnailMergeTask(mlirFiles: Seq[Path], concatMLIR: Path, mergedMLIR: Path) extends LongnailBaseTask {
|
class LongnailMergeTask(mlirFiles: Seq[Path], concatMLIR: Path, mergedMLIR: Path) extends LongnailBaseTask[LongnailMergeTask] {
|
||||||
override def validate(): Unit = {
|
override def validate(): Unit = {
|
||||||
super.validate()
|
super.validate()
|
||||||
|
mlirFiles.foreach(f => assert(f.isMLIRFile, s"Input file $f does not exist or is not an MLIR file"))
|
||||||
}
|
}
|
||||||
|
|
||||||
override def execute(): Unit = {
|
override def execute(): Unit = {
|
||||||
@@ -15,6 +18,6 @@ class LongnailMergeTask(mlirFiles: Seq[Path], concatMLIR: Path, mergedMLIR: Path
|
|||||||
runExecutable(EXECUTABLE,
|
runExecutable(EXECUTABLE,
|
||||||
"--merge-multiple-isaxes", concatMLIR,
|
"--merge-multiple-isaxes", concatMLIR,
|
||||||
"-o", mergedMLIR
|
"-o", mergedMLIR
|
||||||
)
|
)(LOG_DIR / "longnail_merge.log")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,38 +1,24 @@
|
|||||||
package com.minres.tgc.hammer.tasks.longnail
|
package com.minres.tgc.hammer.tasks.longnail
|
||||||
|
|
||||||
import com.minres.tgc.hammer.options.*
|
import com.minres.tgc.hammer.Global.LOG_DIR
|
||||||
import com.minres.tgc.hammer.tasks.longnail.LongnailBaseTask
|
import com.minres.tgc.hammer.tasks.longnail.LongnailBaseTask
|
||||||
import os.Path
|
import com.minres.tgc.hammer.util.FileUtils.isMLIRFile
|
||||||
|
import os.*
|
||||||
|
|
||||||
class LongnailScheduleTask(isaxMLIR: Path, coreDatasheet: Path, params: SchedulingParameters) extends LongnailBaseTask {
|
class LongnailScheduleTask(isaxMLIR: Path, coreDatasheet: Path, params: SchedulingParameters) extends LongnailBaseTask[LongnailScheduleTask] {
|
||||||
|
|
||||||
override def validate(): Unit = {
|
override def validate(): Unit = {
|
||||||
super.validate()
|
super.validate()
|
||||||
|
assert(isMLIRFile(isaxMLIR), "Input file does not exist")
|
||||||
|
assert(isFile(coreDatasheet), "Core datasheet does not exist")
|
||||||
}
|
}
|
||||||
|
|
||||||
override def execute(): Unit = {
|
override def execute(): Unit = {
|
||||||
/*runExecutable(EXECUTABLE,
|
|
||||||
"--lower-coredsl-to-lil",
|
|
||||||
"--max-unroll-factor", params.maxLoopUnrollFactor,
|
|
||||||
"--schedule-lil",
|
|
||||||
"--datasheet", params.base.coreDatasheet,
|
|
||||||
"--library", params.base.cellLibrary,
|
|
||||||
"--opTyLibrary", params.base.opTyLibrary,
|
|
||||||
"--clockTime", params.base.clockPeriod,
|
|
||||||
"--schedulingAlgo", params.base.schedulingAlgo,
|
|
||||||
"--solver", params.base.ilpSolver,
|
|
||||||
"--schedulingTimeout", params.schedulingTimeout,
|
|
||||||
"--schedRefineTimeout", params.schedulingRefineTimeout,
|
|
||||||
"--solSelKconfPath", params.schedulingKconf,
|
|
||||||
"--verbose", params.base.verbose.toString,
|
|
||||||
"-o", params.schedulingMLIR,
|
|
||||||
isaxMLIR
|
|
||||||
)*/
|
|
||||||
runExecutable(EXECUTABLE,
|
runExecutable(EXECUTABLE,
|
||||||
"--lower-coredsl-to-lil",
|
"--lower-coredsl-to-lil",
|
||||||
params.getToolParameters,
|
params.getToolParameters,
|
||||||
isaxMLIR
|
isaxMLIR
|
||||||
)
|
)(LOG_DIR / "longnail_schedule.log")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,27 +1,34 @@
|
|||||||
package com.minres.tgc.hammer.tasks.longnail
|
package com.minres.tgc.hammer.tasks.longnail
|
||||||
|
|
||||||
|
import com.minres.tgc.hammer.util.FileUtils.*
|
||||||
|
import com.minres.tgc.hammer.Global.OUT_DIR
|
||||||
import com.minres.tgc.hammer.options.*
|
import com.minres.tgc.hammer.options.*
|
||||||
import os.Path
|
|
||||||
import org.rogach.scallop.*
|
import org.rogach.scallop.*
|
||||||
|
import os.Path
|
||||||
|
|
||||||
class SchedulingParameters extends OptionGroup {
|
class SchedulingParameters extends OptionGroup {
|
||||||
override def name: String = "Longnail Scheduling Args"
|
override def name: String = "Longnail Scheduling Args"
|
||||||
|
|
||||||
add(new CommandGroup("--prepare-schedule-lil") {
|
class PrepareOptions extends CommandGroup("--prepare-schedule-lil") {
|
||||||
choiceS(Seq("LEGACY", "MS", "PAMS", "PARAMS", "MI_MS", "MI_PAMS", "MI_PARAMS"), name = "schedulingAlgo", default = Some("LEGACY"))
|
val schedulingAlgo = choiceS(Seq("LEGACY", "MS", "PAMS", "PARAMS", "MI_MS", "MI_PAMS", "MI_PARAMS"), name = "schedulingAlgo", default = () => Some("LEGACY"), descr =
|
||||||
value[Path](cliName = "cellLibrary", toolName = "library")
|
"Scheduling algorithm used by Longnail; Modulo Scheduling (MS) can be extended with Predicate-aware (PA) and Resource-aware (RA), Inter-Instruction sharing is activated in the MI variants")
|
||||||
valueS[Path](name = "opTyLibrary")
|
val cellLibrary = value[Path](cliName = "cellLibrary", toolName = "library", descr = "The cell library used by Longnail (example: longnail/test/LILToHW/longnail*.yaml")
|
||||||
})
|
val opTyLibrary = valueS[Path](name = "opTyLibrary", descr = "The operator type model used for detailed data (e.g. from OL SKY130 in longnail/opTyLibraries/OL2.yaml)")
|
||||||
|
check(cellLibrary)(checkPathIsFile)
|
||||||
add(new CommandGroup("--schedule-lil") {
|
check(opTyLibrary)(checkPathIsFile)
|
||||||
valueS[Int](name = "schedulingTimeout")
|
}
|
||||||
value[Int](cliName = "schedulingRefineTimeout", toolName = "schedRefineTimeout")
|
val prepare = add(new PrepareOptions)
|
||||||
value[Path](cliName = "schedulingKconf", toolName = "solSelKconfPath")
|
|
||||||
choice(Seq("CBC", "GLPK", "SCIP", "HIGHS", "GUROBI", "CPLEX", "XPRESS", "COPT"), cliName = "ilpSolver", toolName = "solver", default = Some("CBC"))
|
class ScheduleOptions extends CommandGroup("--schedule-lil") {
|
||||||
toggle(cliName = "verboseSched", toolName = "verbose")
|
val schedulingTimeout = valueS[Int](name = "schedulingTimeout", default = () => Some(10), descr = "Longnail scheduling timeout in seconds")
|
||||||
value[Int](cliName = "clockPeriod", toolName = "clockTime")
|
val schedulingRefineTimeout = value[Int](cliName = "schedulingRefineTimeout", toolName = "schedRefineTimeout", default = () => Some(10), descr = "Longnail schedule refinement timeout in seconds")
|
||||||
})
|
val schedulingSelectionFile = value[Path](cliName = "schedulingKconf", toolName = "solSelKconfPath", default = () => Some(OUT_DIR / "scheduling.KConfig"), descr = "Path for the created KConfig file for selecting a scheduling solution")
|
||||||
|
val ilpSolver = choice(Seq("CBC", "GLPK", "SCIP", "HIGHS", "GUROBI", "CPLEX", "XPRESS", "COPT"), cliName = "ilpSolver", toolName = "solver", default = () => Some("CBC"), descr = "The ILP solver used by Longnail; currently only CBC is tested")
|
||||||
value[Int](cliName = "--maxLoopUnrollFactor", toolName = "max-unroll-factor")
|
val verboseSched = toggle(cliName = "verboseSched", toolName = "verbose", descr = "Enable verbose ILP solver messages")
|
||||||
value[Path](cliName = "--schedulingMLIR", toolName = "o")
|
val clockPeriod = value[Int](cliName = "clockPeriod", toolName = "clockTime", descr = "The target clock period; uses same time unit as delays in opTyLibrary", default = () => Some(10))
|
||||||
|
}
|
||||||
|
val schedule = add(new ScheduleOptions)
|
||||||
|
|
||||||
|
val maxLoopUnrollFactor = value[Int](cliName = "maxLoopUnrollFactor", toolName = "--max-unroll-factor", default = () => Some(16), descr = "Longnail max loop unroll factor")
|
||||||
|
val schedulingSolutionFile = value[Path](cliName = "schedulingMLIR", toolName = "--o", default = () => Some(OUT_DIR / "scheduling_solutions.mlir"), descr = "Output file with different scheduling solutions")
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,3 @@
|
|||||||
|
package com.minres.tgc.hammer.util
|
||||||
|
|
||||||
|
case class AssertException() extends Exception
|
@@ -0,0 +1,39 @@
|
|||||||
|
package com.minres.tgc.hammer.util
|
||||||
|
|
||||||
|
import com.minres.tgc.hammer.Global.BASE_DIR
|
||||||
|
import org.rogach.scallop.{ScallopOption, Util, ValueConverter, listArgConverter, singleArgConverter}
|
||||||
|
import os.*
|
||||||
|
|
||||||
|
import scala.collection.IterableOps
|
||||||
|
|
||||||
|
object FileUtils {
|
||||||
|
def changeExtension(path: os.Path, newExt: String): os.Path = {
|
||||||
|
val baseName = path.baseName
|
||||||
|
val newName = s"$baseName.$newExt"
|
||||||
|
path / os.up / newName
|
||||||
|
}
|
||||||
|
|
||||||
|
extension (x: os.Path)
|
||||||
|
def isMLIRFile: Boolean = isFile(x) && x.ext == "mlir"
|
||||||
|
def isCoreDSLFile: Boolean = isFile(x) && x.ext == "core_desc"
|
||||||
|
|
||||||
|
extension (x: String)
|
||||||
|
def asPath(relative: Path): Path = {
|
||||||
|
Path(x, relative)
|
||||||
|
}
|
||||||
|
def path(): Path = asPath(BASE_DIR)
|
||||||
|
|
||||||
|
extension [T <: Iterable](x: IterableOps[String, T, T[String]])
|
||||||
|
def asPath(relative: Path): T[Path] = {
|
||||||
|
x.map(_.asPath(relative))
|
||||||
|
}
|
||||||
|
def path(): T[Path] = asPath(BASE_DIR)
|
||||||
|
|
||||||
|
implicit val osPathRelBaseConverter: ValueConverter[Path] = {
|
||||||
|
singleArgConverter[Path](_.asPath(BASE_DIR))
|
||||||
|
}
|
||||||
|
implicit val osPathRelBaseListConverter: ValueConverter[List[Path]] = {
|
||||||
|
listArgConverter[Path](_.asPath(BASE_DIR))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,16 @@
|
|||||||
|
package com.minres.tgc.hammer.util
|
||||||
|
|
||||||
|
import com.typesafe.scalalogging.Logger
|
||||||
|
|
||||||
|
import scala.reflect.ClassTag
|
||||||
|
|
||||||
|
trait Logging[T: ClassTag] {
|
||||||
|
protected val log: Logger = Logger[T]
|
||||||
|
|
||||||
|
protected def assert(boolean: Boolean, msg: String): Unit = {
|
||||||
|
if (!boolean) {
|
||||||
|
log.error(msg)
|
||||||
|
throw AssertException()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,8 @@
|
|||||||
|
package com.minres.tgc.hammer.util
|
||||||
|
|
||||||
|
trait ValidationBase[O[_]] {
|
||||||
|
def check[T](option: O[T])(check: Checker[T]): Unit
|
||||||
|
def checkPred[T, U](condOpt: O[U], cond: U => Boolean)(option: O[T])(check: Checker[T]): Unit
|
||||||
|
def checkIf[T](condOpt: O[Boolean])(option: O[T])(check: Checker[T]): Unit = checkPred(condOpt, identity)(option)(check)
|
||||||
|
def checkNotIf[T](condOpt: O[Boolean])(option: O[T])(check: Checker[T]): Unit = checkPred(condOpt, !_)(option)(check)
|
||||||
|
}
|
@@ -0,0 +1,44 @@
|
|||||||
|
package com.minres.tgc.hammer.util
|
||||||
|
|
||||||
|
import org.rogach.scallop.{ScallopOption, Util}
|
||||||
|
|
||||||
|
|
||||||
|
type Checker[T] = ScallopOption[T] => Either[String,Unit]
|
||||||
|
|
||||||
|
trait ValidationUtils { this: ValidationBase[?] =>
|
||||||
|
|
||||||
|
|
||||||
|
private def checkPredicate[T](option: ScallopOption[T], predicate: T => Boolean, format: String): Either[String,Unit] = {
|
||||||
|
option.toOption.map {
|
||||||
|
case o if predicate(o) => Right(())
|
||||||
|
case o => Left(Util.format(format, o))
|
||||||
|
}.getOrElse(Right(()))
|
||||||
|
}
|
||||||
|
|
||||||
|
private def checkPredicateIt[T, CC <: Iterable](option: ScallopOption[CC[T]], predicate: T => Boolean, format: String): Either[String,Unit] = {
|
||||||
|
option.toOption.map(options => {
|
||||||
|
val problems = options.filterNot(predicate)
|
||||||
|
problems match {
|
||||||
|
case Nil => Right(())
|
||||||
|
case problem :: Nil => Left(Util.format(format, problem))
|
||||||
|
case _ => Left(s"Multiple: ${Util.format(format, problems.mkString(", "))}")
|
||||||
|
}
|
||||||
|
}).getOrElse(Right(()))
|
||||||
|
}
|
||||||
|
|
||||||
|
def checkPathExists: Checker[os.Path] = path => checkPredicate(path, os.exists, "File '%s' does not exist")
|
||||||
|
def checkPathDoesNotExist: Checker[os.Path] = path => checkPredicate(path, !os.exists(_), "File '%s' already exists")
|
||||||
|
def checkPathIsFile: Checker[os.Path] = path => checkPredicate(path, os.isFile, "File '%s' is not a file")
|
||||||
|
def checkPathIsDir: Checker[os.Path] = path => checkPredicate(path, os.isDir, "File '%s' is not a directory")
|
||||||
|
|
||||||
|
|
||||||
|
def checkPathsExists: Checker[List[os.Path]] = path => checkPredicateIt(path, os.exists, "File(s) '%s' do not exist")
|
||||||
|
def checkPathsDoesNotExists: Checker[List[os.Path]] = path => checkPredicateIt(path, !os.exists(_), "File(s) '%s' already exist")
|
||||||
|
def checkPathsIsFile: Checker[List[os.Path]] = path => checkPredicateIt(path, os.isFile, "File(s) '%s' are not files")
|
||||||
|
def checkPathsIsDir: Checker[List[os.Path]] = path => checkPredicateIt(path, os.isDir, "File(s) '%s' are not directories")
|
||||||
|
|
||||||
|
def checkPathIsCoreDSL: Checker[os.Path] = path => checkPredicate(path, p => p.ext == "core_desc", "File '%s' is not a CoreDSL file")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
1
wsTemplate/.envrc
Normal file
1
wsTemplate/.envrc
Normal file
@@ -0,0 +1 @@
|
|||||||
|
source source.sh
|
32
wsTemplate/README.md
Normal file
32
wsTemplate/README.md
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
# TGC Hammer Workspace
|
||||||
|
|
||||||
|
This is a workspace for using the TGC Hammer toolchain for ISAX-HLS and integration into TGC cores.
|
||||||
|
The required tools and executables are all located in the base TGC-Hammer repo from which this workspace was created.
|
||||||
|
|
||||||
|
To use the toolchain just enter this directory, `direnv` will then automatically setup all required environment variables.
|
||||||
|
Alternatively you can execute `source source.sh`.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
The executable for the toolchain is called `tgc-hammer`, it features various subcommands for different tasks.
|
||||||
|
The two main ones `isaxHLS` and `isaxCore` are described in the sections below.
|
||||||
|
|
||||||
|
It also features additionally subcommands for individual steps (e.g. just translate CoreDSL to MLIR), for more information see `tgc-hammer --help`.
|
||||||
|
|
||||||
|
### isaxHLS
|
||||||
|
|
||||||
|
This subcommand takes one or multiple input files describing the ISAXES and runs the Longnail HLS tool to create SystemVerilog respresentations for them.
|
||||||
|
|
||||||
|
The base command looks like this: `tgc-hammer isaxHLS -c VexRiscv --useMinIISolution isax.core_desc`
|
||||||
|
|
||||||
|
The `-c` or `--core` option is required, it provides Longnail with the Core Datasheet detailing core-specific scheduling information.
|
||||||
|
|
||||||
|
Currently the `--useMinIISolution` option is also required, manually selecting scheduling solutions is WIP.
|
||||||
|
|
||||||
|
The command ends with the input files. If multiple ones are specified, they are merged using Longnail before the scheduling and HLS. Both CoreDSL and already translated MLIR files are supported (also mixed).
|
||||||
|
|
||||||
|
For additional options see `tgc-hammer isaxHLS --help`.
|
||||||
|
|
||||||
|
### isaxCore
|
||||||
|
|
||||||
|
WIP
|
257
wsTemplate/examples/Keccak.core_desc
Normal file
257
wsTemplate/examples/Keccak.core_desc
Normal file
@@ -0,0 +1,257 @@
|
|||||||
|
InstructionSet Zx_keccak {
|
||||||
|
architectural_state {
|
||||||
|
unsigned int XLEN=32;
|
||||||
|
register unsigned<XLEN> X[32] [[is_main_reg]];
|
||||||
|
extern unsigned<8> MEM[1 << XLEN] [[is_main_mem]];
|
||||||
|
|
||||||
|
const unsigned int RV_CAUSE_ILLEGAL_INSTRUCTION = 0x02;
|
||||||
|
|
||||||
|
register unsigned<64> K[32];
|
||||||
|
register unsigned<8> LFSR;
|
||||||
|
}
|
||||||
|
functions {
|
||||||
|
|
||||||
|
unsigned<64> ROL64(unsigned<64> val, unsigned<64> shift) {
|
||||||
|
return (val << shift) | (val >> (64 - shift));
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned<64> readLane(unsigned int x, unsigned int y) {
|
||||||
|
return K[x + 5 * y];
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned<64> XORLane(unsigned int x, unsigned int y, unsigned<64> value) {
|
||||||
|
K[x + 5 * y] = K[x + 5 * y] ^ value;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned<64> writeLane(unsigned int x, unsigned int y, unsigned<64> value) {
|
||||||
|
K[x + 5 * y] = value;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned<1> LFSR86540() {
|
||||||
|
unsigned<1> result = (unsigned<1>)(LFSR & 0x01);
|
||||||
|
if ((LFSR & 0x80) != 0) {
|
||||||
|
// Primitive polynomial over GF(2): x^8+x^6+x^5+x^4+1
|
||||||
|
LFSR = (LFSR << 1) ^ 0x71;
|
||||||
|
} else {
|
||||||
|
LFSR = LFSR << 1;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
instructions {
|
||||||
|
// Custom-0 Opcode
|
||||||
|
LK64 {
|
||||||
|
encoding: imm[11:0] :: rs1[4:0] :: 3'b000 :: rd[4:0] :: 7'b0001011;
|
||||||
|
assembly: "{name(rd)}, {imm}({name(rs1)})";
|
||||||
|
behavior: {
|
||||||
|
unsigned<XLEN> load_address = (unsigned<XLEN>)(X[rs1] + (signed<12>)imm);
|
||||||
|
K[rd]= MEM[load_address+7:load_address];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LK128 {
|
||||||
|
encoding: imm[11:0] :: rs1[4:0] :: 3'b010 :: rd[4:0] :: 7'b0001011;
|
||||||
|
assembly: "{name(rd)}, {imm}({name(rs1)})";
|
||||||
|
behavior: if(rd%2 == 1) ; else {
|
||||||
|
unsigned<XLEN> base_address = (unsigned<XLEN>)(X[rs1] + (signed<12>)imm);
|
||||||
|
for(unsigned int i = 0; i<2; i++){
|
||||||
|
unsigned<XLEN> load_address = (unsigned<XLEN>)(base_address + 8*i);
|
||||||
|
K[rd+i]= MEM[load_address+7:load_address];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LK256 {
|
||||||
|
encoding: imm[11:0] :: rs1[4:0] :: 3'b100 :: rd[4:0] :: 7'b0001011;
|
||||||
|
assembly: "{name(rd)}, {imm}({name(rs1)})";
|
||||||
|
behavior: if(rd%4 == 1) ; else {
|
||||||
|
unsigned<XLEN> base_address = (unsigned<XLEN>)(X[rs1] + (signed<12>)imm);
|
||||||
|
for(unsigned int i = 0; i<4; i++){
|
||||||
|
unsigned<XLEN> load_address = (unsigned<XLEN>)(base_address + 8*i);
|
||||||
|
K[rd+i]= MEM[load_address+7:load_address];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LK512 {
|
||||||
|
encoding: imm[11:0] :: rs1[4:0] :: 3'b110 :: rd[4:0] :: 7'b0001011;
|
||||||
|
assembly: "{name(rd)}, {imm}({name(rs1)})";
|
||||||
|
behavior: if(rd%8 == 1) ; else {
|
||||||
|
unsigned<XLEN> base_address = (unsigned<XLEN>)(X[rs1] + (signed<12>)imm);
|
||||||
|
for(unsigned int i = 0; i<8; i++){
|
||||||
|
unsigned<XLEN> load_address = (unsigned<XLEN>)(base_address + 8*i);
|
||||||
|
K[rd+i]= MEM[load_address+7:load_address];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SK64 {
|
||||||
|
encoding: imm[11:5] :: rs2[4:0] :: rs1[4:0] :: 3'b001 :: imm[4:0] :: 7'b0001011;
|
||||||
|
assembly: "{name(rs2)}, {imm}({name(rs1)})";
|
||||||
|
behavior: {
|
||||||
|
unsigned<XLEN> store_address = (unsigned<XLEN>)(X[rs1] + (signed)imm);
|
||||||
|
MEM[store_address+7:store_address] = K[rs2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SK128 {
|
||||||
|
encoding: imm[11:5] :: rs2[4:0] :: rs1[4:0] :: 3'b011 :: imm[4:0] :: 7'b0001011;
|
||||||
|
assembly: "{name(rs2)}, {imm}({name(rs1)})";
|
||||||
|
behavior: if(rs2%2 == 1) ; else {
|
||||||
|
unsigned<XLEN> base_address = (unsigned<XLEN>)(X[rs1] + (signed<12>)imm);
|
||||||
|
for(unsigned int i = 0; i<2; i++){
|
||||||
|
unsigned<XLEN> store_address = (unsigned<XLEN>)(base_address + 8*i);
|
||||||
|
MEM[store_address+7:store_address] = K[rs2+i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SK256 {
|
||||||
|
encoding: imm[11:5] :: rs2[4:0] :: rs1[4:0] :: 3'b101 :: imm[4:0] :: 7'b0001011;
|
||||||
|
assembly: "{name(rs2)}, {imm}({name(rs1)})";
|
||||||
|
behavior: if(rs2%4 == 1) ; else {
|
||||||
|
unsigned<XLEN> base_address = (unsigned<XLEN>)(X[rs1] + (signed<12>)imm);
|
||||||
|
for(unsigned int i = 0; i<4; i++){
|
||||||
|
unsigned<XLEN> store_address = (unsigned<XLEN>)(base_address + 8*i);
|
||||||
|
MEM[store_address+7:store_address] = K[rs2+i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SK512 {
|
||||||
|
encoding: imm[11:5] :: rs2[4:0] :: rs1[4:0] :: 3'b111 :: imm[4:0] :: 7'b0001011;
|
||||||
|
assembly: "{name(rs2)}, {imm}({name(rs1)})";
|
||||||
|
behavior: if(rs2%8 == 1) ; else {
|
||||||
|
unsigned<XLEN> base_address = (unsigned<XLEN>)(X[rs1] + (signed<12>)imm);
|
||||||
|
for(unsigned int i = 0; i<8; i++){
|
||||||
|
unsigned<XLEN> store_address = (unsigned<XLEN>)(base_address + 8*i);
|
||||||
|
MEM[store_address+7:store_address] = K[rs2+i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
KECCAK_THETA {
|
||||||
|
encoding: 7'b0000000 :: 5'b00000 :: 5'b00000 :: 3'b000 :: 5'b00000 :: 7'b0001011;
|
||||||
|
assembly: "keccak.theta";
|
||||||
|
behavior: {
|
||||||
|
// Temporary storage for C array (5 columns)
|
||||||
|
unsigned<64> C0, C1, C2, C3, C4;
|
||||||
|
unsigned<64> D;
|
||||||
|
|
||||||
|
// Compute the parity of the columns
|
||||||
|
C0 = readLane(0, 0) ^ readLane(0, 1) ^ readLane(0, 2) ^ readLane(0, 3) ^ readLane(0, 4);
|
||||||
|
C1 = readLane(1, 0) ^ readLane(1, 1) ^ readLane(1, 2) ^ readLane(1, 3) ^ readLane(1, 4);
|
||||||
|
C2 = readLane(2, 0) ^ readLane(2, 1) ^ readLane(2, 2) ^ readLane(2, 3) ^ readLane(2, 4);
|
||||||
|
C3 = readLane(3, 0) ^ readLane(3, 1) ^ readLane(3, 2) ^ readLane(3, 3) ^ readLane(3, 4);
|
||||||
|
C4 = readLane(4, 0) ^ readLane(4, 1) ^ readLane(4, 2) ^ readLane(4, 3) ^ readLane(4, 4);
|
||||||
|
|
||||||
|
// Apply theta effect to column 0
|
||||||
|
D = C4 ^ ROL64(C1, 1);
|
||||||
|
XORLane(0, 0, D);
|
||||||
|
XORLane(0, 1, D);
|
||||||
|
XORLane(0, 2, D);
|
||||||
|
XORLane(0, 3, D);
|
||||||
|
XORLane(0, 4, D);
|
||||||
|
|
||||||
|
// Apply theta effect to column 1
|
||||||
|
D = C0 ^ ROL64(C2, 1);
|
||||||
|
XORLane(1, 0, D);
|
||||||
|
XORLane(1, 1, D);
|
||||||
|
XORLane(1, 2, D);
|
||||||
|
XORLane(1, 3, D);
|
||||||
|
XORLane(1, 4, D);
|
||||||
|
|
||||||
|
// Apply theta effect to column 2
|
||||||
|
D = C1 ^ ROL64(C3, 1);
|
||||||
|
XORLane(2, 0, D);
|
||||||
|
XORLane(2, 1, D);
|
||||||
|
XORLane(2, 2, D);
|
||||||
|
XORLane(2, 3, D);
|
||||||
|
XORLane(2, 4, D);
|
||||||
|
|
||||||
|
// Apply theta effect to column 3
|
||||||
|
D = C2 ^ ROL64(C4, 1);
|
||||||
|
XORLane(3, 0, D);
|
||||||
|
XORLane(3, 1, D);
|
||||||
|
XORLane(3, 2, D);
|
||||||
|
XORLane(3, 3, D);
|
||||||
|
XORLane(3, 4, D);
|
||||||
|
|
||||||
|
// Apply theta effect to column 4
|
||||||
|
D = C3 ^ ROL64(C0, 1);
|
||||||
|
XORLane(4, 0, D);
|
||||||
|
XORLane(4, 1, D);
|
||||||
|
XORLane(4, 2, D);
|
||||||
|
XORLane(4, 3, D);
|
||||||
|
XORLane(4, 4, D);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
KECCAK_RHO_PI {
|
||||||
|
encoding: 7'b0000000 :: 5'b00000 :: 5'b00000 :: 3'b001 :: 5'b00000 :: 7'b0001011;
|
||||||
|
assembly: "keccak.rhopi";
|
||||||
|
behavior: {
|
||||||
|
unsigned<64> current, temp;
|
||||||
|
unsigned int x, y;
|
||||||
|
unsigned int r;
|
||||||
|
unsigned int Y;
|
||||||
|
|
||||||
|
// Start at coordinates (1, 0)
|
||||||
|
x = 1;
|
||||||
|
y = 0;
|
||||||
|
current = readLane(x, y);
|
||||||
|
|
||||||
|
// Iterate over ((0 1)(2 3))^t * (1 0) for 0 ≤ t ≤ 23
|
||||||
|
for(unsigned int t = 0; t < 24; t++) {
|
||||||
|
// Compute the rotation constant r = (t+1)(t+2)/2
|
||||||
|
r = ((t + 1) * (t + 2) / 2) % 64;
|
||||||
|
|
||||||
|
// Compute ((0 1)(2 3)) * (x y)
|
||||||
|
Y = (2 * x + 3 * y) % 5;
|
||||||
|
x = y;
|
||||||
|
y = Y;
|
||||||
|
|
||||||
|
// Swap current and state(x,y), and rotate
|
||||||
|
temp = readLane(x, y);
|
||||||
|
writeLane(x, y, ROL64(current, r));
|
||||||
|
current = temp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
KECCAK_CHI {
|
||||||
|
encoding: 7'b0000000 :: 5'b00000 :: 5'b00000 :: 3'b010 :: 5'b00000 :: 7'b0001011;
|
||||||
|
assembly: "keccak.chi";
|
||||||
|
behavior: {
|
||||||
|
unsigned<64> T0, T1, T2, T3, T4;
|
||||||
|
unsigned int y;
|
||||||
|
for(y = 0; y < 5; y++) {
|
||||||
|
/* Take a copy of the plane */
|
||||||
|
T0 = readLane(0, y);
|
||||||
|
T1 = readLane(1, y);
|
||||||
|
T2 = readLane(2, y);
|
||||||
|
T3 = readLane(3, y);
|
||||||
|
T4 = readLane(4, y);
|
||||||
|
/* Compute χ on the plane */
|
||||||
|
writeLane(0, y, T0 ^ (~T1 & T2));
|
||||||
|
writeLane(1, y, T1 ^ (~T2 & T3));
|
||||||
|
writeLane(2, y, T2 ^ (~T3 & T4));
|
||||||
|
writeLane(3, y, T3 ^ (~T4 & T0));
|
||||||
|
writeLane(4, y, T4 ^ (~T0 & T1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
KECCAK_IOTA {
|
||||||
|
encoding: 7'b0000000 :: 5'b00000 :: 5'b00000 :: 3'b011 :: 5'b00000 :: 7'b0001011;
|
||||||
|
assembly: "keccak.iota";
|
||||||
|
behavior: {
|
||||||
|
unsigned int j;
|
||||||
|
for (j = 0; j < 7; j++) {
|
||||||
|
unsigned int bitPosition = (unsigned int)((1 << j) - 1);
|
||||||
|
if (LFSR86540()) {
|
||||||
|
XORLane(0, 0, (unsigned<64>)1 << bitPosition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
KECCAK_LFSR_RESET {
|
||||||
|
encoding: 7'b0000000 :: 5'b00000 :: 5'b00000 :: 3'b100 :: 5'b00000 :: 7'b0001011;
|
||||||
|
assembly: "keccak.lfsr.reset";
|
||||||
|
behavior: {
|
||||||
|
LFSR = 0x01;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
79
wsTemplate/examples/LWC.core_desc
Normal file
79
wsTemplate/examples/LWC.core_desc
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
|
||||||
|
InstructionSet Zxlwc_ascon {
|
||||||
|
architectural_state {
|
||||||
|
unsigned int XLEN=32;
|
||||||
|
register unsigned<XLEN> X[32] [[is_main_reg]];
|
||||||
|
}
|
||||||
|
|
||||||
|
functions {
|
||||||
|
|
||||||
|
unsigned<64> _ror64(unsigned<64> val, unsigned<64> shift_amount) {
|
||||||
|
return (val >> shift_amount) | (val << (64 - shift_amount));
|
||||||
|
}
|
||||||
|
// ROT_0 = { 19, 61, 1, 10, 7 }
|
||||||
|
unsigned<64> rot_0(unsigned<5> imm){
|
||||||
|
unsigned<64> ret;
|
||||||
|
if(imm == 0)
|
||||||
|
ret = 19;
|
||||||
|
else if(imm==1)
|
||||||
|
ret = 61;
|
||||||
|
else if(imm==2)
|
||||||
|
ret = 1;
|
||||||
|
else if(imm==3)
|
||||||
|
ret = 10;
|
||||||
|
else if(imm==4)
|
||||||
|
ret = 7;
|
||||||
|
else ret = 0;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
// ROT_1 = { 28, 39, 6, 17, 41 }
|
||||||
|
unsigned<64> rot_1(unsigned<5> imm){
|
||||||
|
unsigned<64> ret;
|
||||||
|
if(imm == 0)
|
||||||
|
ret = 28;
|
||||||
|
else if(imm==1)
|
||||||
|
ret = 39;
|
||||||
|
else if(imm==2)
|
||||||
|
ret = 6;
|
||||||
|
else if(imm==3)
|
||||||
|
ret = 17;
|
||||||
|
else if(imm==4)
|
||||||
|
ret = 41;
|
||||||
|
else ret = 0;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
instructions{
|
||||||
|
ASCON_SIGMA_LO [[enable=XLEN==32]] {
|
||||||
|
encoding: 2'b00 :: imm[4:0] :: rs2[4:0] :: rs1[4:0] :: 3'b111 :: rd[4:0] :: 7'b0101011;
|
||||||
|
behavior: {
|
||||||
|
unsigned<32> x_hi = (unsigned<32>)X[rs2];
|
||||||
|
unsigned<32> x_lo = (unsigned<32>)X[rs1];
|
||||||
|
unsigned<64> x = x_hi :: x_lo;
|
||||||
|
unsigned<64> r = x ^ _ror64(x, rot_0(imm)) ^ _ror64(x, rot_1(imm));
|
||||||
|
X[rd] = (unsigned<XLEN>)r[31:0];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
ASCON_SIGMA_HI [[enable=XLEN==32]] {
|
||||||
|
encoding: 2'b01 :: imm[4:0] :: rs2[4:0] :: rs1[4:0] :: 3'b111 :: rd[4:0] :: 7'b0101011;
|
||||||
|
behavior: {
|
||||||
|
unsigned<32> x_hi = (unsigned<32>)X[rs2];
|
||||||
|
unsigned<32> x_lo = (unsigned<32>)X[rs1];
|
||||||
|
unsigned<64> x = x_hi :: x_lo;
|
||||||
|
unsigned<64> r = x ^ _ror64(x, rot_0(imm)) ^ _ror64(x, rot_1(imm));
|
||||||
|
X[rd] = (unsigned<XLEN>)r[63:32];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ASCON_SIGMA [[enable=XLEN==64]] {
|
||||||
|
encoding: 2'b10 :: imm[4:0] :: 5'b00000 :: rs1[4:0] :: 3'b110 :: rd[4:0] :: 7'b0101011;
|
||||||
|
behavior: {
|
||||||
|
unsigned<64> x = X[rs1];
|
||||||
|
unsigned<64> r = x ^ _ror64(x, rot_0(imm)) ^ _ror64(x, rot_1(imm));
|
||||||
|
X[rd] = (unsigned<XLEN>)r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user