Add logging and update validation

This commit is contained in:
2025-10-06 16:27:06 +02:00
parent 22a1f31683
commit be691c96be
22 changed files with 209 additions and 93 deletions

View File

@@ -1,6 +1,5 @@
package com.minres.tgc.hammer
import com.minres.tgc.hammer.FileUtils.*
import os.Path
object Global {

View File

@@ -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()
}
}
}

View File

@@ -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()
}

View File

@@ -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"}")
}
}

View File

@@ -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()}")
}
}

View File

@@ -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()}")
}
}

View File

@@ -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)
}

View File

@@ -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()}")
}
}

View File

@@ -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)
}

View File

@@ -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)
}
}

View File

@@ -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)
}
}

View File

@@ -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 = {

View File

@@ -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

View File

@@ -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)

View File

@@ -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 = {

View File

@@ -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 = {

View File

@@ -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,

View File

@@ -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)")
})
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)
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")
})
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)
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 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")
}

View File

@@ -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))
}
}

View File

@@ -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)
}
}

View File

@@ -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)
}

View File

@@ -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")
}