Commit e2440b54 authored by Bruno Ferres's avatar Bruno Ferres
Browse files

Merge branch 'v1.1.0' into 'master'

v1.1.0: new way to configure and import QECE in external projects

See merge request !1
parents 4e8cec41 b9ec569a
// See README.md for license details.
ThisBuild / scalaVersion := "2.12.12"
ThisBuild / version := "1.0.0"
ThisBuild / version := "1.1.0"
ThisBuild / organization := "fr.tima"
parallelExecution in Test := true
......@@ -54,12 +54,6 @@ addCommandAlias("lintCheck", "; scalastyle; test:scalastyle" +
"; scalafmtCheck; test:scalafmtCheck" +
"; scalafix --check; test:scalafix --check")
/* ---- Stack overflow exception occurs when doc is generated ---- */
addCommandAlias("disableDocGeneration", "set publishArtifact in(Compile, packageDoc) := false")
addCommandAlias("enableDocGeneration", "set publishArtifact in(Compile, packageDoc) := true")
addCommandAlias("quickPublish", "disableDocGeneration; publishLocal; enableDocGeneration")
addCommandAlias("disableParallel", "set parallelExecution in Test := false;")
addCommandAlias("enableParallel", "set parallelExecution in Test := true;")
......
......@@ -19,38 +19,38 @@ package qece
import logger.{LazyLogging, Logger}
trait CustomLogging extends LazyLogging {
Logger.setLevel(this.getClass, ProjectConfig.logLevel)
Logger.setLevel(this.getClass, QeceContext.logger.logLevel)
def mylog(str: String) = {
ProjectConfig.enableLog match {
protected def mylog(str: String) = {
QeceContext.logger.enableLog match {
case true => logger.warn(s"[${Utils.getTime}] $str")
case false =>
}
}
def mytrace(str: String) = {
ProjectConfig.enableTrace match {
protected def mytrace(str: String) = {
QeceContext.logger.enableTrace match {
case true => logger.warn(s"[${Utils.getTime}]" + " " * 8 + s"$str")
case false =>
}
}
def mydebug(str: String) = {
ProjectConfig.enableDebug match {
protected def mydebug(str: String) = {
QeceContext.logger.enableDebug match {
case true => logger.warn(s"[DEBUG][${Utils.getTime}] $str")
case false =>
}
}
def mysyncdebug(str: String) = {
ProjectConfig.enableSyncDebug match {
protected def mysyncdebug(str: String) = {
QeceContext.logger.enableSyncDebug match {
case true => logger.warn(s"[SYNCHRO][${Utils.getTime}] $str")
case false =>
}
}
def timeError(str: String) = {
ProjectConfig.enableError match {
protected def timeError(str: String) = {
QeceContext.logger.enableError match {
case true => logger.error(s"[${Utils.getTime}] $str")
case false =>
}
......
/** Part of QECE (Quick Exploration using Chisel Estimators) program. Copyright (C) <2021> <Bruno FERRES>
*
* This program is developped at TIMA, Grenoble. Please contact Bruno FERRES (bruno.ferres@grenoble-inp.org) or Olivier
* MULLER (olivier.muller@univ-grenoble-alpes.fr) for more informations.
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see
* <https://www.gnu.org/licenses/>.
*/
package qece
import logger.LogLevel
import qece.estimation.macros.MacroBlock
import qece.estimation.macros.components._
/** Config object for the project. */
object ProjectConfig {
// val config = ConfigFactory.load()
/** Define how parallelisation is supposed to be managed */
val useScalaParSeq = false
val activeWaiting: Option[Int] = None //Some(60000)
/** Define default target for the estimators. */
val defaultBoard = "Zedboard"
/** Define synthesis timeout, using the `timeout format`. */
val synthTimeout = "2h"
/** Define the number of threads to be used in the different processes. */
val numThread = 4
/** Define used vivado version */
val vivadoVersion = "2017.3"
/** Define number of threads used in each backend call. Put to 1 as syntheses are difficult to parallelize. */
val backendThreadNumber = "1"
/** Define number of thread used for simulation */
val simulationThreadNumber = 16
/** Define which backend to use in simulation based estimators */
val simulationBackend = "treadle"
/** Define number of thread used for synthesis */
val synthesisThreadNumber = 1
/** Define target period for synthesis, in ns */
val period = 10
/** ============ Macro configurations ================= */
// TODO: manage default here
// val defaultMacroSeq = MacroSeq(List(MacUnit, MemoryBlock))
/** MacUnit config. */
/** max absorbed registers. */
val maxDspRegister = 3
/** Memory block register. */
/** max address amount, as a power of two */
val maxMemoryBlockAddr = 16
/** Mux config. */
/** maximum number of input for a mux block, as a power of two */
val maxMuxBlockInput = 8
/** ============== Characterization =================== */
/** Specify the bitwidths to use for characterization. This may use expert knowledge about the target, to cope with
* transfert effect on the technology.
*/
val characterizationBitMap =
Map[MacroBlock[_], List[Int]](MemoryBlock -> List(1, 8, 32, 64, 128, 256, 512, 1024))
.withDefaultValue(List(1, 2, 4, 8, 15, 16, 32, 64, 128, 256))
val characterizeRegisters = true
/** Basic bitwidth to run characterization on. */
val baseWidth = 1
/** ============== Loggers =================== */
/** Define custom log level. */
//TODO: proper way to handle this
val logLevel = LogLevel.Warn
val enableTrace = true
val enableLog = true
val enableError = true
val enableDebug = false
val enableSyncDebug = false
/** Define chisel log level. */
val chiselLogLevel = LogLevel.None
/** Stack tace depth */
val stackDepth = 0
/** ============== Estimation =================== */
/** Enable or not invalid estimation. */
val enableInvalidEstimation = true
/** ============== Generation =================== */
/** Default default generation directory for emitted files. */
val genDirectory = "chisel_gen/"
/** Define csv separator. */
val csvSeparator = ";"
/** Force synthesis disable for debug purposes. */
val disableSynthesis = false
/** Force implementation disable for debug purposes. */
val disableImplementation = false
/** ========== Exploration configurations =============== */
val defaultCost = 0.0
/** ========== Transform configurations =============== */
// TODO: manage default here
// lazy val defaultTransformSeq = TransformSeq.resources
/** ========== Timeouts configuration =============== */
// define timeout for the different transforms
val totalEstimationTimeout = "4h"
val replaceMacroTimeout = "30min"
val primitiveReportTimeout = "30min"
val estimateResourceTimeout = "Inf"
val endpointReportTimeout = "2h"
val criticalPathTimeout = "Inf"
// useless as timeout already done at backend level
val synthesisTransformTimeout = "Inf"
}
/** Part of QECE (Quick Exploration using Chisel Estimators) program. Copyright (C) <2021> <Bruno FERRES>
*
* This program is developped at TIMA, Grenoble. Please contact Bruno FERRES (bruno.ferres@grenoble-inp.org) or Olivier
* MULLER (olivier.muller@univ-grenoble-alpes.fr) for more informations.
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see
* <https://www.gnu.org/licenses/>.
*/
package qece
import logger.LogLevel
import qece.estimation.macros.MacroBlock
import qece.estimation.macros.components._
import scala.util.DynamicVariable
/** Configuration of the QECE core, i.e. estimation and exploration features.
*
* @todo manage the default transforms here
* @param enableInvalidEstimation
* either fail on invalid estimation, or just continue and mark as failure
* @param defaultCost
* default cost for estimated metrics (e.g. on estimation failure)
*/
case class CoreConfig(
enableInvalidEstimation: Boolean = true,
defaultCost: Double = 0.0
)
/** Configuration of the parallelism management.
*
* @param useScalaParSeq
* either use scala built-in ParSeq (which resulted in starving threads) or custom ThreadPool
* @param activeWaiting
* if useScalaParSeq == false, define the ThreadPool behavior, either using event driven waiting, or active waiting
* @param numThread
* define the number of threads to be used in the different processes (can be overrided by more specific parameters)
*/
case class ParallelConfig(
useScalaParSeq: Boolean = false,
activeWaiting: Option[Int] = None,
numThread: Int = 4
)
/** Configuration of the FPGA backend.
*
* @param targetBoard
* define the name of the target board, with respect to the ones defined in the backend package
* @param vivadoVersion
* define the vivado version to be used for synthesis
* @param backendThreadNumber
* define the number of threads used in each FPGA backend call.
* set to 1 as syntheses are difficult to parallelize efficiently
* @param period
* define the target period for FPGA backend calls (in ns)
*/
case class BackendConfig(
targetBoard: String = "Zedboard",
vivadoVersion: String = "2017.3",
backendThreadNumber: String = "1",
targetPeriod: Int = 10
)
/** Configuration of the simulation backend.
*
* @param simulationBackend
* define which backend to be used for simulations
*/
case class SimulationConfig(
backend: String = "treadle"
)
/** Configuration of the characterization system, including the macro block mechanism.
*
* @todo manage defaultMacroSeq configuration here ?
* @param maxDspRegister
* max. amount of registers that can be absorbed in a MacUnit pattern
* @param maxMemoryBlockAddr
* max. number of addresses available on MemoryBlock (as a power of two)
* @param maxMuxBlockInput
* max. number of inputs for a MuxBlock (as a power of two)
* @param characterizationBitMap
* specify the bitwidths to use for characterization. This may use expert knowledge about the target, to cope with
* transfert effects on the technology.
* @param characterizeRegisters
* either use backend to characterize the registers, or use naive estimation through primitive counting
* @param baseWidth
* base bitwidth to run characterization on
*/
case class CharacterizationConfig(
maxDspRegister: Int = 3,
maxMemoryBlockAddr: Int = 16,
maxMuxBlockInput: Int = 8,
characterizationBitMap: Map[MacroBlock[_], List[Int]] =
Map[MacroBlock[_], List[Int]](MemoryBlock -> List(1, 8, 32, 64, 128, 256, 512, 1024))
.withDefaultValue(List(1, 2, 4, 8, 15, 16, 32, 64, 128, 256)),
characterizeRegisters: Boolean = true,
baseWidth: Int = 1
)
/** Configuration of the logging system.
*
* @param logLevel
* global logging level
* @param enableTrace
* @param enableLog
* @param enableError
* @param enableDebug
* @param enableSyncDebug
* @param chiselLogLevel
* define chisel logging level
* @param stackDepth
* define the size of the stack trace to be displayed on exception handling
*/
case class LoggerConfig(
logLevel: LogLevel.Value = LogLevel.Warn,
enableTrace: Boolean = true,
enableLog: Boolean = true,
enableError: Boolean = true,
enableDebug: Boolean = false,
enableSyncDebug: Boolean = false,
chiselLogLevel: LogLevel.Value = LogLevel.None,
stackDepth: Int = 0
)
/** Configuration of the emission system.
*
* @param genDirectory
* default generation directory for the emitted files
* @param csvSeparator
* define the csv separator used for result files
* @param disableSynthesis
* force synthesis disable (for debug purposes)
* @param disableImplementation
* force implementation (P&R) disable (for debug purposes)
*/
case class EmissionConfig(
basePath: String = "chisel_gen/",
targetPath: String = "default/",
csvSeparator: String = ";",
disableSynthesis: Boolean = false,
disableImplementation: Boolean = false
)
/** Various timeouts configuration
*
* @param totalEstimation
* timeout for a whole estimation process
* @param replaceMacro
* timeout for the replace macro transform
* @param primitiveReport
* timeout for the primitive report transform
* @param estimateResource
* timeout for the estimate resource transform
* @param endpointReport
* timeout for the endpoint report transform
* @param criticalPath
* timeout for the critical path estimation transform
* @param synthesis
* define synthesis timeout, using the `timeout format`
* @param synthesisTransform
* useless as the timeout is already defined at backend level
*/
case class TimeoutConfig(
totalEstimation: String = "4h",
replaceMacro: String = "30min",
primitiveReport: String = "30min",
estimateResource: String = "Inf",
endpointReport: String = "2h",
criticalPath: String = "Inf",
synthesis: String = "2h",
synthesisTransform: String = "Inf"
)
/** Global configuration for the project.
*
* @param core
* QECE core configuration, i.e. estimation and exploration features
* @param parallel
* thread management configuration
* @param backend
* FPGA backend configuration
* @param simulation
* simulation backend configuration
* @param characterization
* characterization system configuration
* @param logger
* logging system configuration
* @param emission
* emission system configuration
* @param timeout
* timeout system configuration
*/
case class QeceConfig(
core: CoreConfig = CoreConfig(),
parallel: ParallelConfig = ParallelConfig(),
backend: BackendConfig = BackendConfig(),
simulation: SimulationConfig = SimulationConfig(),
characterization: CharacterizationConfig = CharacterizationConfig(),
logger: LoggerConfig = LoggerConfig(),
emission: EmissionConfig = EmissionConfig(),
timeout: TimeoutConfig = TimeoutConfig()
) {
/** Create a copy of this by update members.
*
* @param core
* function to update the core config
* @param parallel
* function to update the parallel config
* @param backend
* function to update the backend config
* @param simulation
* function to update the simulation config
* @param characterization
* function to update the characterization config
* @param logger
* function to update the logger config
* @param emission
* function to update the emission config
* @param timeout
* function to update the timeout config
*/
def funcCopy(
core: CoreConfig => CoreConfig = (m => m),
parallel: ParallelConfig => ParallelConfig = (m => m),
backend: BackendConfig => BackendConfig = (m => m),
simulation: SimulationConfig => SimulationConfig = (m => m),
characterization: CharacterizationConfig => CharacterizationConfig = (m => m),
logger: LoggerConfig => LoggerConfig = (m => m),
emission: EmissionConfig => EmissionConfig = (m => m),
timeout: TimeoutConfig => TimeoutConfig = (m => m)
): QeceConfig = this.copy(
core = core(this.core),
parallel = parallel(this.parallel),
backend = backend(this.backend),
simulation = simulation(this.simulation),
characterization = characterization(this.characterization),
logger = logger(this.logger),
emission = emission(this.emission),
timeout = timeout(this.timeout)
)
}
/** A global object used to build the global configuration of the project.
*
* A DynamicVariable is used to allow overriding of the basic configuration, as well as some syntactic sugar
* to allow users to do it in a concise way.
*/
object QeceContext extends DynamicVariable(QeceConfig()){
def core = value.core
def parallel = value.parallel
def backend = value.backend
def simulation = value.simulation
def characterization = value.characterization
def logger = value.logger
def emission = value.emission
def timeout = value.timeout
/** Run a block of code in a context where the context is updated.
*
* @param core
* function to update the core config
* @param parallel
* function to update the parallel config
* @param backend
* function to update the backend config
* @param simulation
* function to update the simulation config
* @param characterization
* function to update the characterization config
* @param logger
* function to update the logger config
* @param emission
* function to update the emission config
* @param timeout
* function to update the timeout config
*/
def withValue[S](
core: CoreConfig => CoreConfig = (m => m),
parallel: ParallelConfig => ParallelConfig = (m => m),
backend: BackendConfig => BackendConfig = (m => m),
simulation: SimulationConfig => SimulationConfig = (m => m),
characterization: CharacterizationConfig => CharacterizationConfig = (m => m),
logger: LoggerConfig => LoggerConfig = (m => m),
emission: EmissionConfig => EmissionConfig = (m => m),
timeout: TimeoutConfig => TimeoutConfig = (m => m)
)(thunk: => S): S = withValue(
this.copy(
core = core,
parallel = parallel,
backend = backend,
simulation = simulation,
characterization = characterization,
logger = logger,
emission = emission,
timeout = timeout
)
)(thunk)
/** Create a copy of the current configuration, and potentially update it.
*
* @param core
* function to update the core config
* @param parallel
* function to update the parallel config
* @param backend
* function to update the backend config
* @param simulation
* function to update the simulation config
* @param characterization
* function to update the characterization config
* @param logger
* function to update the logger config
* @param emission
* function to update the emission config
* @param timeout
* function to update the timeout config
*/
def copy(
core: CoreConfig => CoreConfig = (m => m),
parallel: ParallelConfig => ParallelConfig = (m => m),
backend: BackendConfig => BackendConfig = (m => m),
simulation: SimulationConfig => SimulationConfig = (m => m),
characterization: CharacterizationConfig => CharacterizationConfig = (m => m),
logger: LoggerConfig => LoggerConfig = (m => m),
emission: EmissionConfig => EmissionConfig = (m => m),
timeout: TimeoutConfig => TimeoutConfig = (m => m)
): QeceConfig = this.value.funcCopy(
core = core,
parallel = parallel,
backend = backend,
simulation = simulation,
characterization = characterization,
logger = logger,
emission = emission,
timeout = timeout
)
}
......@@ -18,8 +18,15 @@ package qece
import java.util.{Calendar, Date}
/** Helpers that can be helpful for logging purposes.
*
* @todo maybe it should not be part of the API.
*/
object Utils {
/** retrieve the current date as a String. */
def getDate: String = new Date().toString
/** retrieve the current date as a Date. */
def getTime: Date = Calendar.getInstance().getTime()
}
......@@ -16,7 +16,7 @@
*/
package qece.backend
import qece.{Utils, ProjectConfig, CustomLogging}
import qece.{Utils, QeceContext, CustomLogging}
import qece.backend.exceptions._
import qece.backend.utils._
......@@ -30,17 +30,20 @@ object FPGA {
case _ => throw UnknownBoardException(s"Did not found board with name $name")
}
}
def default: FPGA = FPGA(ProjectConfig.defaultBoard)
def default: FPGA = FPGA(QeceContext.backend.targetBoard)
}
/** FPGA class for backend access. */
abstract class FPGA extends CustomLogging {
/** Name of this particular [[FPGA]]. */
val name = this.getClass.getSimpleName
val nbThread = ProjectConfig.backendThreadNumber
val timeoutOpt = Seq("-s", "SIGKILL", "-k", "1s", ProjectConfig.synthTimeout)
/** Run full synthesis, and report both timing and resources
* @param root
protected val nbThread = QeceContext.backend.backendThreadNumber
protected val timeoutOpt = Seq("-s", "SIGKILL", "-k", "1s", QeceContext.timeout.synthesis)