Add logging and update validation
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
package com.minres.tgc.hammer
|
||||
|
||||
import com.minres.tgc.hammer.FileUtils.*
|
||||
import os.Path
|
||||
|
||||
object Global {
|
||||
|
@@ -11,7 +11,7 @@ object Main {
|
||||
var conf: HammerConf = uninitialized
|
||||
|
||||
def main(args: Array[String]): Unit = {
|
||||
var logger = Logger("TGCHammer")
|
||||
val logger = Logger("TGCHammer")
|
||||
conf = new HammerConf(args.toIndexedSeq)
|
||||
|
||||
val logLevel = conf.verbose() match {
|
||||
@@ -22,14 +22,23 @@ object Main {
|
||||
}
|
||||
LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME).asInstanceOf[ch.qos.logback.classic.Logger].setLevel(logLevel)
|
||||
|
||||
os.makeDir(Global.OUT_DIR)
|
||||
os.makeDir(Global.TMP_DIR)
|
||||
logger.trace(s"Creating output directory ${Global.OUT_DIR}")
|
||||
if (conf.emptyOutputDir()) os.remove.all(Global.OUT_DIR)
|
||||
os.makeDir.all(Global.OUT_DIR)
|
||||
logger.trace(s"Creating temp directory ${Global.TMP_DIR}")
|
||||
os.makeDir.all(Global.TMP_DIR)
|
||||
conf.subcommand match {
|
||||
case Some(c: MySubcommand) =>
|
||||
logger.info(s"Executing subcommand ${c.name}")
|
||||
val tasks = c.getRequiredTasks
|
||||
logger.debug(s"Subcommand requires ${tasks.size} tasks")
|
||||
logger.info(s"Validating tasks")
|
||||
tasks.foreach(_.validate())
|
||||
logger.info(s"Executing tasks")
|
||||
tasks.foreach(_.execute())
|
||||
case _ =>
|
||||
logger.error(s"Found no subcommand, see help below")
|
||||
conf.printHelp()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -2,16 +2,25 @@ package com.minres.tgc.hammer.cli
|
||||
|
||||
import org.rogach.scallop.*
|
||||
import os.Path
|
||||
import com.minres.tgc.hammer.FileUtils.*
|
||||
import com.minres.tgc.hammer.util.FileUtils.*
|
||||
|
||||
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()
|
||||
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")
|
||||
addSubcommand(new LongnailCommand)
|
||||
addSubcommand(new TreenailCommand)
|
||||
addSubcommand(new LongnailMergeCommand)
|
||||
addSubcommand(new LongnailSchedCommand)
|
||||
|
||||
requireSubcommand()
|
||||
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()
|
||||
}
|
||||
|
@@ -1,19 +1,22 @@
|
||||
package com.minres.tgc.hammer.cli
|
||||
|
||||
import com.minres.tgc.hammer.FileUtils.*
|
||||
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 {
|
||||
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
|
||||
@@ -34,11 +37,17 @@ class LongnailCommand extends MySubcommand("isaxHLS") with CoreSelection {
|
||||
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, OUT_DIR)
|
||||
tasks += LongnailHLSTask(schedParams.schedulingSolutionFile(), None, verilogOutput)
|
||||
} else {
|
||||
???
|
||||
}
|
||||
tasks.toSeq
|
||||
}
|
||||
|
||||
override def cleanup(): Unit = {
|
||||
log.info(s"Finished, verilog output in ${OUT_DIR / "verilog"}")
|
||||
}
|
||||
}
|
||||
|
@@ -3,11 +3,12 @@ package com.minres.tgc.hammer.cli
|
||||
import com.minres.tgc.hammer.tasks.{CopyTask, Task, TreenailTask}
|
||||
import org.rogach.scallop.*
|
||||
import os.Path
|
||||
import com.minres.tgc.hammer.FileUtils.*
|
||||
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") {
|
||||
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()
|
||||
@@ -19,16 +20,24 @@ class LongnailMergeCommand extends MySubcommand("mergeISAX") {
|
||||
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,17 +1,19 @@
|
||||
package com.minres.tgc.hammer.cli
|
||||
|
||||
import com.minres.tgc.hammer.FileUtils.*
|
||||
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.{Task, TreenailTask}
|
||||
import com.minres.tgc.hammer.tasks.longnail.SchedulingParameters
|
||||
import com.minres.tgc.hammer.util.{Logging, ValidationUtils}
|
||||
import org.rogach.scallop.*
|
||||
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, descr = "One or multiple input files; Both .core_desc as well as .mlir are supported (also mixed)")
|
||||
val schedParams: SchedulingParameters = addConfigElement(new SchedulingParameters)
|
||||
|
||||
check(inputFiles)(checkPathsIsFile)
|
||||
|
||||
banner(
|
||||
"""Generate Scheduling information for the provided ISAXes using Longnail
|
||||
@@ -19,15 +21,23 @@ class LongnailSchedCommand extends MySubcommand("scheduleISAX") with CoreSelecti
|
||||
|""".stripMargin)
|
||||
|
||||
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 treenailTasks = coreDSLFiles.map(i => TreenailTask(i, TMP_DIR / s"${i.baseName}.mlir"))
|
||||
val allMlirFiles = mlirFiles ++ treenailTasks.map(_.output)
|
||||
if (allMlirFiles.size == 1) {
|
||||
log.debug(s"Single input file, only creating schedule task...")
|
||||
treenailTasks :+ LongnailScheduleTask(allMlirFiles.head, getCoreDatasheet, schedParams)
|
||||
} else {
|
||||
val mergedInput = TMP_DIR / "merged.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)
|
||||
}
|
||||
}
|
||||
|
||||
override def cleanup(): Unit = {
|
||||
log.info(s"Finished, scheduling solutions in ${schedParams.schedulingSolutionFile()}")
|
||||
log.info(s"Scheduling selection file (KConf) in ${schedParams.schedule.schedulingSelectionFile()}")
|
||||
}
|
||||
}
|
||||
|
@@ -3,55 +3,28 @@ package com.minres.tgc.hammer.cli
|
||||
import com.minres.tgc.hammer.options.ConfigElement
|
||||
import com.minres.tgc.hammer.tasks.Task
|
||||
import org.rogach.scallop.{ScallopOption, ScallopOptionGroup, Subcommand, Util}
|
||||
import com.minres.tgc.hammer.util.{Checker, ValidationBase}
|
||||
|
||||
type Checker[T] = ScallopOption[T] => Either[String,Unit]
|
||||
|
||||
abstract class MySubcommand(name: String) extends Subcommand(name) {
|
||||
abstract class MySubcommand(val name: String) extends Subcommand(name) with ValidationBase[ScallopOption] {
|
||||
protected val mainGroup: ScallopOptionGroup = group()
|
||||
|
||||
def getRequiredTasks: Seq[Task]
|
||||
def cleanup(): Unit
|
||||
|
||||
def addConfigElement[T <: ConfigElement](elem: T): T = {
|
||||
elem.init(this, null)
|
||||
elem
|
||||
}
|
||||
|
||||
def checkPred[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(()))
|
||||
|
||||
def check[T](option: ScallopOption[T])(check: Checker[T]): Unit = {
|
||||
addValidation(check(option))
|
||||
}
|
||||
|
||||
def checkPredIt[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 => checkPred(path, os.exists, "File '%s' does not exist")
|
||||
def checkPathDoesNotExist: Checker[os.Path] = path => checkPred(path, !os.exists(_), "File '%s' already exists")
|
||||
def checkPathIsFile: Checker[os.Path] = path => checkPred(path, os.isFile, "File '%s' is not a file")
|
||||
def checkPathIsDir: Checker[os.Path] = path => checkPred(path, os.isDir, "File '%s' is not a directory")
|
||||
|
||||
|
||||
def checkPathsExists: Checker[List[os.Path]] = path => checkPredIt(path, os.exists, "File(s) '%s' do not exist")
|
||||
def checkPathsDoesNotExists: Checker[List[os.Path]] = path => checkPredIt(path, !os.exists(_), "File(s) '%s' already exist")
|
||||
def checkPathsIsFile: Checker[List[os.Path]] = path => checkPredIt(path, os.isFile, "File(s) '%s' are not files")
|
||||
def checkPathsIsDir: Checker[List[os.Path]] = path => checkPredIt(path, os.isDir, "File(s) '%s' are not directories")
|
||||
|
||||
def check[T](option: ScallopOption[T])(check: Checker[T]): Unit = addValidation(check(option))
|
||||
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(())
|
||||
}
|
||||
}
|
||||
def checkIf[T](condOpt: ScallopOption[Boolean])(option: ScallopOption[T])(check: Checker[T]): Unit = checkPred(condOpt, identity)(option)(check)
|
||||
def checkNotIf[T](condOpt: ScallopOption[Boolean])(option: ScallopOption[T])(check: Checker[T]): Unit = checkPred(condOpt, !_)(option)(check)
|
||||
}
|
||||
|
@@ -1,16 +1,18 @@
|
||||
package com.minres.tgc.hammer.cli
|
||||
|
||||
import com.minres.tgc.hammer.FileUtils.*
|
||||
import com.minres.tgc.hammer.util.FileUtils.*
|
||||
import com.minres.tgc.hammer.tasks.{Task, TreenailTask}
|
||||
import com.minres.tgc.hammer.util.{Logging, ValidationUtils}
|
||||
import org.rogach.scallop.*
|
||||
import os.Path
|
||||
|
||||
class TreenailCommand extends MySubcommand("parseCoreDSL") {
|
||||
class TreenailCommand extends MySubcommand("parseCoreDSL") with Logging[TreenailCommand] with ValidationUtils {
|
||||
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("isax.mlir".path()), descr = "The .mlir file containing the converted ISAX")
|
||||
val overwrite: ScallopOption[Boolean] = toggle()
|
||||
|
||||
check(coreDSL)(checkPathIsFile)
|
||||
check(coreDSL)(checkPathIsCoreDSL)
|
||||
checkNotIf(overwrite)(output)(checkPathDoesNotExist)
|
||||
|
||||
banner(
|
||||
@@ -23,4 +25,8 @@ class TreenailCommand extends MySubcommand("parseCoreDSL") {
|
||||
TreenailTask(coreDSL(), output())
|
||||
)
|
||||
}
|
||||
|
||||
override def cleanup(): Unit = {
|
||||
log.info(s"Finished, MLIR output in ${output()}")
|
||||
}
|
||||
}
|
||||
|
@@ -7,7 +7,7 @@ import scala.compiletime.uninitialized
|
||||
|
||||
trait BaseOption[T](using conv: ValueConverter[T]) extends ConfigElement {
|
||||
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 = {
|
||||
scallop = createScallop(scallopConf, group)
|
||||
}
|
||||
|
@@ -11,5 +11,6 @@ class CommandGroup(name: String) extends BaseGroup {
|
||||
|
||||
override def init(scallopConf: ScallopConf, group: ScallopOptionGroup): Unit = {
|
||||
options.foreach(_.init(scallopConf, group))
|
||||
initValidation(scallopConf)
|
||||
}
|
||||
}
|
||||
|
@@ -1,18 +1,39 @@
|
||||
package com.minres.tgc.hammer.options
|
||||
|
||||
|
||||
import com.minres.tgc.hammer.util.{Checker, ValidationBase, ValidationUtils}
|
||||
import org.rogach.scallop.{ScallopConf, ScallopOptionGroup, ValueConverter}
|
||||
import os.Shellable
|
||||
|
||||
import scala.collection.mutable
|
||||
|
||||
trait BaseGroup extends ConfigElement {
|
||||
trait BaseGroup extends ConfigElement with ValidationBase[BaseOption] with ValidationUtils {
|
||||
protected val options = mutable.ListBuffer[ConfigElement]()
|
||||
protected def add[T <: ConfigElement](option: T): T = {
|
||||
options += option
|
||||
option
|
||||
}
|
||||
|
||||
def initValidation(scallopConf: ScallopConf): Unit = {
|
||||
validations.foreach(fn => scallopConf.addValidation(fn()))
|
||||
}
|
||||
|
||||
private var validations: List[() => Either[String, Unit]] = Nil
|
||||
|
||||
private def addValidation(fn: () => Either[String, Unit]): Unit = {
|
||||
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))
|
||||
|
||||
@@ -36,5 +57,6 @@ trait OptionGroup extends BaseGroup {
|
||||
def init(scallopConf: ScallopConf, g: ScallopOptionGroup): Unit = {
|
||||
val group = scallopConf.group(name)
|
||||
options.foreach(_.init(scallopConf, group))
|
||||
initValidation(scallopConf)
|
||||
}
|
||||
}
|
||||
|
@@ -4,7 +4,7 @@ import os.*
|
||||
|
||||
case class CopyTask(from: Path, to: Path) extends Task {
|
||||
override def validate(): Unit = {
|
||||
|
||||
assert(isFile(from), "Input file does not exist")
|
||||
}
|
||||
|
||||
override def execute(): Unit = {
|
||||
|
@@ -1,8 +1,9 @@
|
||||
package com.minres.tgc.hammer.tasks
|
||||
|
||||
import com.minres.tgc.hammer.util.Logging
|
||||
import os.{Path, Shellable}
|
||||
|
||||
trait Task {
|
||||
trait Task extends Logging[Task] {
|
||||
def validate(): Unit
|
||||
def execute(): Unit
|
||||
|
||||
|
@@ -1,7 +1,7 @@
|
||||
package com.minres.tgc.hammer.tasks
|
||||
|
||||
import com.minres.tgc.hammer.Global
|
||||
|
||||
import com.minres.tgc.hammer.util.FileUtils.*
|
||||
import os.*
|
||||
|
||||
case class TreenailTask(coreDSLInput: Path, output: Path) extends Task {
|
||||
@@ -9,6 +9,7 @@ case class TreenailTask(coreDSLInput: Path, output: Path) extends Task {
|
||||
|
||||
override def validate(): Unit = {
|
||||
assert(isFile(EXECUTABLE), "Treenail Executable is missing, build Treenail")
|
||||
assert(isCoreDSLFile(coreDSLInput), "Input file does not exist")
|
||||
}
|
||||
override def execute(): Unit = {
|
||||
runExecutable(EXECUTABLE, "-o", output, coreDSLInput)
|
||||
|
@@ -1,10 +1,14 @@
|
||||
package com.minres.tgc.hammer.tasks.longnail
|
||||
|
||||
import os.Path
|
||||
import com.minres.tgc.hammer.util.FileUtils.*
|
||||
import os.*
|
||||
|
||||
class LongnailHLSTask(schedulingSolutionFile: Path, schedulingSelectionFile: Option[Path], outDirectory: Path) extends LongnailBaseTask {
|
||||
override def validate(): Unit = {
|
||||
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 = {
|
||||
|
@@ -1,10 +1,12 @@
|
||||
package com.minres.tgc.hammer.tasks.longnail
|
||||
|
||||
import os.Path
|
||||
import com.minres.tgc.hammer.util.FileUtils.*
|
||||
import os.*
|
||||
|
||||
class LongnailMergeTask(mlirFiles: Seq[Path], concatMLIR: Path, mergedMLIR: Path) extends LongnailBaseTask {
|
||||
override def validate(): Unit = {
|
||||
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 = {
|
||||
|
@@ -1,33 +1,18 @@
|
||||
package com.minres.tgc.hammer.tasks.longnail
|
||||
|
||||
import com.minres.tgc.hammer.options.*
|
||||
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 {
|
||||
|
||||
override def validate(): Unit = {
|
||||
super.validate()
|
||||
assert(isMLIRFile(isaxMLIR), "Input file does not exist")
|
||||
assert(isFile(coreDatasheet), "Core datasheet does not exist")
|
||||
}
|
||||
|
||||
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,
|
||||
"--lower-coredsl-to-lil",
|
||||
params.getToolParameters,
|
||||
|
@@ -1,6 +1,6 @@
|
||||
package com.minres.tgc.hammer.tasks.longnail
|
||||
|
||||
import com.minres.tgc.hammer.FileUtils.*
|
||||
import com.minres.tgc.hammer.util.FileUtils.*
|
||||
import com.minres.tgc.hammer.Global.OUT_DIR
|
||||
import com.minres.tgc.hammer.options.*
|
||||
import org.rogach.scallop.*
|
||||
@@ -9,22 +9,26 @@ import os.Path
|
||||
class SchedulingParameters extends OptionGroup {
|
||||
override def name: String = "Longnail Scheduling Args"
|
||||
|
||||
add(new CommandGroup("--prepare-schedule-lil") {
|
||||
choiceS(Seq("LEGACY", "MS", "PAMS", "PARAMS", "MI_MS", "MI_PAMS", "MI_PARAMS"), name = "schedulingAlgo", default = Some("LEGACY"), descr =
|
||||
class PrepareOptions extends CommandGroup("--prepare-schedule-lil") {
|
||||
val schedulingAlgo = choiceS(Seq("LEGACY", "MS", "PAMS", "PARAMS", "MI_MS", "MI_PAMS", "MI_PARAMS"), name = "schedulingAlgo", default = Some("LEGACY"), descr =
|
||||
"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")
|
||||
value[Path](cliName = "cellLibrary", toolName = "library", descr = "The cell library used by Longnail (example: longnail/test/LILToHW/longnail*.yaml")
|
||||
valueS[Path](name = "opTyLibrary", descr = "The operator type model used for detailed data (e.g. from OL SKY130 in longnail/opTyLibraries/OL2.yaml)")
|
||||
})
|
||||
|
||||
add(new CommandGroup("--schedule-lil") {
|
||||
valueS[Int](name = "schedulingTimeout", default = Some(10), descr = "Longnail scheduling timeout in seconds")
|
||||
value[Int](cliName = "schedulingRefineTimeout", toolName = "schedRefineTimeout", default = Some(10), descr = "Longnail schedule refinement timeout in seconds")
|
||||
value[Path](cliName = "schedulingKconf", toolName = "solSelKconfPath", default = Some(OUT_DIR / "scheduling.KConfig"), descr = "Path for the created KConfig file for selecting a scheduling solution")
|
||||
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")
|
||||
toggle(cliName = "verboseSched", toolName = "verbose", descr = "Enable verbose ILP solver messages")
|
||||
value[Int](cliName = "clockPeriod", toolName = "clockTime", descr = "The target clock period; uses same time unit as delays in opTyLibrary")
|
||||
})
|
||||
|
||||
value[Int](cliName = "--maxLoopUnrollFactor", toolName = "max-unroll-factor", default = Some(16), descr = "Longnail max loop unroll factor")
|
||||
val schedulingSolutionFile: ValueOption[Path] = value[Path](cliName = "--schedulingMLIR", toolName = "o", default = Some(OUT_DIR / "scheduling_solutions.mlir"), descr = "Output file with different scheduling solutions")
|
||||
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)
|
||||
check(opTyLibrary)(checkPathIsFile)
|
||||
}
|
||||
val prepare = add(new PrepareOptions)
|
||||
|
||||
class ScheduleOptions extends CommandGroup("--schedule-lil") {
|
||||
val schedulingTimeout = valueS[Int](name = "schedulingTimeout", default = Some(10), descr = "Longnail scheduling timeout in seconds")
|
||||
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")
|
||||
val verboseSched = toggle(cliName = "verboseSched", toolName = "verbose", descr = "Enable verbose ILP solver messages")
|
||||
val clockPeriod = value[Int](cliName = "clockPeriod", toolName = "clockTime", descr = "The target clock period; uses same time unit as delays in opTyLibrary")
|
||||
}
|
||||
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")
|
||||
}
|
||||
|
@@ -1,8 +1,8 @@
|
||||
package com.minres.tgc.hammer
|
||||
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 Global.*
|
||||
|
||||
import scala.collection.IterableOps
|
||||
|
||||
@@ -13,6 +13,10 @@ object FileUtils {
|
||||
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)
|
||||
@@ -31,4 +35,5 @@ object FileUtils {
|
||||
implicit val osPathRelBaseListConverter: ValueConverter[List[Path]] = {
|
||||
listArgConverter[Path](_.asPath(BASE_DIR))
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,13 @@
|
||||
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)
|
||||
}
|
||||
}
|
@@ -0,0 +1,10 @@
|
||||
package com.minres.tgc.hammer.util
|
||||
|
||||
import org.rogach.scallop.ScallopOption
|
||||
|
||||
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")
|
||||
|
||||
|
||||
|
||||
}
|
Reference in New Issue
Block a user