aboutsummaryrefslogtreecommitdiff
path: root/fuzzer/src/main/scala/firrtl/ExprGenParams.scala
diff options
context:
space:
mode:
Diffstat (limited to 'fuzzer/src/main/scala/firrtl/ExprGenParams.scala')
-rw-r--r--fuzzer/src/main/scala/firrtl/ExprGenParams.scala213
1 files changed, 213 insertions, 0 deletions
diff --git a/fuzzer/src/main/scala/firrtl/ExprGenParams.scala b/fuzzer/src/main/scala/firrtl/ExprGenParams.scala
new file mode 100644
index 00000000..909f3a16
--- /dev/null
+++ b/fuzzer/src/main/scala/firrtl/ExprGenParams.scala
@@ -0,0 +1,213 @@
+package firrtl.fuzzer
+
+import firrtl.{Namespace, Utils}
+import firrtl.ir._
+
+import scala.language.higherKinds
+
+/** A set of parameters for randomly generating [[firrtl.ir.Expression Expression]]s
+ */
+sealed trait ExprGenParams {
+
+ /** The maximum levels of nested sub-expressions that may be generated
+ */
+ def maxDepth: Int
+
+ /** The maximum width of any generated expression, including sub-expressions
+ */
+ def maxWidth: Int
+
+ /** A list of frequency/expression generator pairs
+ *
+ * The frequency number determines the probability that the corresponding
+ * generator will be chosen. i.e. for sequece Seq(1 -> A, 2 -> B, 3 -> C),
+ * the probabilities for A, B, and C are 1/6, 2/6, and 3/6 respectively.
+ * This sequency must be non-empty and all frequency numbers must be greater
+ * than zero.
+ */
+ def generators: Seq[(Int, ExprGen[_ <: Expression])]
+
+ /** The set of generated references that don't have a corresponding declaration
+ */
+ protected def unboundRefs: Set[Reference]
+
+ /** The namespace to use for generating new [[firrtl.ir.Reference Reference]]s
+ */
+ protected def namespace: Namespace
+
+ /** Returns a copy of this [[ExprGenParams]] with the maximum depth decremented
+ */
+ protected def decrementDepth: ExprGenParams
+
+ /** Returns a copy of this [[ExprGenParams]] with the maximum depth incremented
+ */
+ protected def incrementDepth: ExprGenParams
+
+ /** Returns a copy of this [[ExprGenParams]] with the reference added to the set of unbound references
+ */
+ protected def withRef(ref: Reference): ExprGenParams
+
+
+ import GenMonad.syntax._
+
+ /** Generator that generates an expression and wraps it in a Module
+ *
+ * The generated references are bound to input ports and the generated
+ * expression is assigned to an output port.
+ */
+ private def exprMod[G[_]: GenMonad]: StateGen[ExprGenParams, G, Module] = {
+ for {
+ width <- StateGen.inspectG((s: ExprGenParams) => ExprGen.genWidth(1, ExprState[ExprGenParams].maxWidth(s)))
+ tpe <- StateGen.liftG(GenMonad.frequency(
+ 2 -> UIntType(width),
+ 2 -> SIntType(width),
+ 1 -> Utils.BoolType
+ ))
+ expr <- ExprState[ExprGenParams].exprGen(tpe)
+ outputPortRef <- tpe match {
+ case UIntType(IntWidth(width)) if width == BigInt(1) => ExprGen.ReferenceGen.boolUIntGen[ExprGenParams, G].get
+ case UIntType(IntWidth(width)) => ExprGen.ReferenceGen.uintGen[ExprGenParams, G].get(width)
+ case SIntType(IntWidth(width)) if width == BigInt(1) => ExprGen.ReferenceGen.boolSIntGen[ExprGenParams, G].get
+ case SIntType(IntWidth(width)) => ExprGen.ReferenceGen.sintGen[ExprGenParams, G].get(width)
+ }
+ unboundRefs <- StateGen.inspect { ExprState[ExprGenParams].unboundRefs(_) }
+ } yield {
+ val outputPort = Port(
+ NoInfo,
+ outputPortRef.name,
+ Output,
+ outputPortRef.tpe
+ )
+ Module(
+ NoInfo,
+ "foo",
+ unboundRefs.flatMap {
+ case ref if ref.name == outputPortRef.name => None
+ case ref => Some(Port(NoInfo, ref.name, Input, ref.tpe))
+ }.toSeq.sortBy(_.name) :+ outputPort,
+ Connect(NoInfo, outputPortRef, expr)
+ )
+ }
+ }
+
+ /** Runs the expression generator once and returns the generated expression
+ * wrapped in a Module and Circuit
+ */
+ def generateSingleExprCircuit[G[_]: GenMonad](): Circuit = {
+ exprMod.map { m =>
+ Circuit(NoInfo, Seq(m), m.name)
+ }.run(this).map(_._2).generate()
+ }
+}
+
+object ExprGenParams {
+
+ private case class ExprGenParamsImp(
+ maxDepth: Int,
+ maxWidth: Int,
+ generators: Seq[(Int, ExprGen[_ <: Expression])],
+ protected val unboundRefs: Set[Reference],
+ protected val namespace: Namespace) extends ExprGenParams {
+
+ protected def decrementDepth: ExprGenParams = this.copy(maxDepth = maxDepth - 1)
+ protected def incrementDepth: ExprGenParams = this.copy(maxDepth = maxDepth + 1)
+ protected def withRef(ref: Reference): ExprGenParams = this.copy(unboundRefs = unboundRefs + ref)
+ }
+
+ /** Constructs an [[ExprGenParams]] with the given parameters
+ */
+ def apply(
+ maxDepth: Int,
+ maxWidth: Int,
+ generators: Seq[(Int, ExprGen[_ <: Expression])]
+ ): ExprGenParams = {
+ require(maxWidth > 0, "maxWidth must be greater than zero")
+ ExprGenParamsImp(
+ maxDepth,
+ maxWidth,
+ generators,
+ Set.empty,
+ Namespace()
+ )
+ }
+
+ import GenMonad.syntax._
+
+ private def combineExprGens[S: ExprState, G[_]: GenMonad](
+ exprGenerators: Seq[(Int, ExprGen[_ <: Expression])]
+ )(tpe: Type): StateGen[S, G, Option[Expression]] = {
+ val boolUIntStateGens = exprGenerators.flatMap {
+ case (freq, gen) => gen.boolUIntGen[S, G].map(freq -> _.widen[Expression])
+ }
+ val uintStateGenFns = exprGenerators.flatMap {
+ case (freq, gen) => gen.uintGen[S, G].map { fn =>
+ (width: BigInt) => freq -> fn(width).widen[Expression]
+ }
+ }
+ val boolSIntStateGens = exprGenerators.flatMap {
+ case (freq, gen) => gen.boolSIntGen[S, G].map(freq -> _.widen[Expression])
+ }
+ val sintStateGenFns = exprGenerators.flatMap {
+ case (freq, gen) => gen.sintGen[S, G].map { fn =>
+ (width: BigInt) => freq -> fn(width).widen[Expression]
+ }
+ }
+ val stateGens: Seq[(Int, StateGen[S, G, Expression])] = tpe match {
+ case Utils.BoolType => boolUIntStateGens
+ case UIntType(IntWidth(width)) => uintStateGenFns.map(_(width))
+ case SIntType(IntWidth(width)) if width.toInt == 1 => boolSIntStateGens
+ case SIntType(IntWidth(width)) => sintStateGenFns.map(_(width))
+ }
+ StateGen { (s: S) =>
+ if (stateGens.isEmpty) {
+ GenMonad[G].const(s -> None)
+ } else if (stateGens.size == 1) {
+ stateGens(0)._2.run(s).map { case (ss, expr) => ss -> Some(expr) }
+ } else {
+ GenMonad.frequency(stateGens: _*).flatMap { stateGen =>
+ stateGen.run(s).map { case (ss, expr) => ss -> Some(expr) }
+ }
+ }
+ }
+ }
+
+ implicit val exprGenParamsExprStateInstance: ExprState[ExprGenParams] = new ExprState[ExprGenParams] {
+ def withRef[G[_]: GenMonad](ref: Reference): StateGen[ExprGenParams, G, Reference] = {
+ StateGen { (s: ExprGenParams) =>
+ val refx = ref.copy(name = s.namespace.newName(ref.name))
+ GenMonad[G].const(s.withRef(refx) -> refx)
+ }
+ }
+ def unboundRefs(s: ExprGenParams): Set[Reference] = s.unboundRefs
+ def maxWidth(s: ExprGenParams): Int = s.maxWidth
+
+ def exprGen[G[_]: GenMonad](tpe: Type): StateGen[ExprGenParams, G, Expression] = {
+ StateGen { (s: ExprGenParams) =>
+
+ val leafGen: Type => StateGen[ExprGenParams, G, Expression] = (tpe: Type) => combineExprGens(Seq(
+ 1 -> ExprGen.LiteralGen,
+ 1 -> ExprGen.ReferenceGen
+ ))(tpe).map(e => e.get) // should be safe because leaf generators are defined for all types
+
+ val branchGen: Type => StateGen[ExprGenParams, G, Expression] = (tpe: Type) => {
+ combineExprGens(s.generators)(tpe).flatMap {
+ case None => leafGen(tpe)
+ case Some(e) => StateGen.pure(e)
+ }
+ }
+
+ if (s.maxDepth > 0) {
+ // for recrusive generators, decrement maxDepth before recursing then increment when finished
+ GenMonad.frequency(
+ 5 -> (branchGen(_)),
+ 1 -> (leafGen(_))
+ ).flatMap(_(tpe).run(s.decrementDepth).map {
+ case (ss, e) => ss.incrementDepth -> e
+ })
+ } else {
+ leafGen(tpe).run(s)
+ }
+ }
+ }
+ }
+}