From 767affa99c35db4e20aae4562600d2d8c6a9416b Mon Sep 17 00:00:00 2001 From: John Wright Date: Wed, 23 Oct 2019 11:10:08 -0700 Subject: [PATCH 01/73] Add a rough draft of the floorplan IR and plumbing to generate annotations --- build.sbt | 28 +++++ floorplan/src/main/scala/Constraints.scala | 106 ++++++++++++++++ floorplan/src/main/scala/IR.scala | 114 ++++++++++++++++++ floorplan/src/main/scala/Serialization.scala | 5 + floorplan/src/main/scala/chisel/API.scala | 4 + .../main/scala/chisel/ChiselAnnotations.scala | 15 +++ .../src/main/scala/firrtl/Annotations.scala | 19 +++ 7 files changed, 291 insertions(+) create mode 100644 floorplan/src/main/scala/Constraints.scala create mode 100644 floorplan/src/main/scala/IR.scala create mode 100644 floorplan/src/main/scala/Serialization.scala create mode 100644 floorplan/src/main/scala/chisel/API.scala create mode 100644 floorplan/src/main/scala/chisel/ChiselAnnotations.scala create mode 100644 floorplan/src/main/scala/firrtl/Annotations.scala diff --git a/build.sbt b/build.sbt index 1a972764..b6b50ed8 100644 --- a/build.sbt +++ b/build.sbt @@ -29,3 +29,31 @@ resolvers ++= Seq( Resolver.sonatypeRepo("releases"), Resolver.mavenLocal ) + +disablePlugins(sbtassembly.AssemblyPlugin) + +lazy val mdf = (project in file("mdf/scalalib")) +lazy val macros = (project in file("macros")) + .dependsOn(mdf) + .settings(commonSettings) + .settings(Seq( + libraryDependencies ++= Seq( + "edu.berkeley.cs" %% "firrtl-interpreter" % "1.2-SNAPSHOT" % Test + ), + mainClass := Some("barstools.macros.MacroCompiler") + )) + .enablePlugins(sbtassembly.AssemblyPlugin) + +lazy val tapeout = (project in file("tapeout")) + .settings(commonSettings) + .settings(Seq( + libraryDependencies ++= Seq( + "io.github.daviddenton" %% "handlebars-scala-fork" % "2.3.0" + ) + )) + .settings(scalacOptions in Test ++= Seq("-language:reflectiveCalls")) + +lazy val floorplan = (project in file("floorplan")) + .settings(commonSettings) + +lazy val root = (project in file(".")).aggregate(macros, tapeout, floorplan) diff --git a/floorplan/src/main/scala/Constraints.scala b/floorplan/src/main/scala/Constraints.scala new file mode 100644 index 00000000..911352eb --- /dev/null +++ b/floorplan/src/main/scala/Constraints.scala @@ -0,0 +1,106 @@ +// See LICENSE for license details +package barstools.floorplan + +sealed trait Unit + +// TODO how to cleanly round down when dividing? +final case class Unitless(value: BigInt) extends Unit { + def *(other: Unitless) = Unitless(this.value * other.value) + def /(other: Unitless) = Unitless(this.value / other.value) + def +(other: Unitless) = Unitless(this.value + other.value) + def -(other: Unitless) = Unitless(this.value - other.value) + + def *(other: LengthUnit) = LengthUnit(this.value * other.value) + def /(other: LengthUnit) = LengthUnit(this.value / other.value) + def +(other: LengthUnit) = LengthUnit(this.value + other.value) + def -(other: LengthUnit) = LengthUnit(this.value - other.value) + + def *(other: AreaUnit) = AreaUnit(this.value * other.value) + def /(other: AreaUnit) = AreaUnit(this.value / other.value) + def +(other: AreaUnit) = AreaUnit(this.value + other.value) + def -(other: AreaUnit) = AreaUnit(this.value - other.value) +} + +final case class LengthUnit(value: BigInt) extends Unit { + def *(other: LengthUnit) = AreaUnit(this.value * other.value) + def /(other: LengthUnit) = Unitless(this.value / other.value) + def +(other: LengthUnit) = LengthUnit(this.value + other.value) + def -(other: LengthUnit) = LengthUnit(this.value - other.value) + + def *(other: Unitless) = LengthUnit(this.value * other.value) + def /(other: Unitless) = LengthUnit(this.value / other.value) +} + +final case class AreaUnit(value: BigInt) extends Unit { + def /(other: LengthUnit) = LengthUnit(this.value / other.value) + + def *(other: Unitless) = AreaUnit(this.value * other.value) + def /(other: Unitless) = AreaUnit(this.value / other.value) + + def /(other: AreaUnit) = Unitless(this.value / other.value) + def +(other: AreaUnit) = AreaUnit(this.value + other.value) + def -(other: AreaUnit) = AreaUnit(this.value - other.value) +} + +sealed trait Constraint[T <: Unit] + +sealed abstract class Constrained[T <: Unit] extends Constraint[T] + +sealed abstract class Unconstrained[T <: Unit] extends Constraint[T] +case object UnconstrainedUnitless extends Unconstrained[Unitless] +case object UnconstrainedLength extends Unconstrained[LengthUnit] +case object UnconstrainedArea extends Unconstrained[AreaUnit] + +sealed abstract class ExactConstraint[T <: Unit] extends Constrained[T] { + def value: T +} +final case class ExactUnitlessConstraint(value: Unitless) extends ExactConstraint[Unitless] +final case class ExactLengthConstraint(value: LengthUnit) extends ExactConstraint[LengthUnit] +final case class ExactAreaConstraint(value: AreaUnit) extends ExactConstraint[AreaUnit] + +sealed abstract class GreaterThanConstraint[T <: Unit] extends Constrained[T] { + def value: T + def inclusive: Boolean +} +final case class GreaterThanUnitlessConstraint(value: Unitless, inclusive: Boolean) extends GreaterThanConstraint[Unitless] +final case class GreaterThanLengthConstraint(value: LengthUnit, inclusive: Boolean) extends GreaterThanConstraint[LengthUnit] +final case class GreaterThanAreaConstraint(value: AreaUnit, inclusive: Boolean) extends GreaterThanConstraint[AreaUnit] + +sealed abstract class LessThanConstraint[T <: Unit] extends Constrained[T] { + def value: T + def inclusive: Boolean +} +final case class LessThanUnitlessConstraint(value: Unitless, inclusive: Boolean) extends LessThanConstraint[Unitless] +final case class LessThanLengthConstraint(value: LengthUnit, inclusive: Boolean) extends LessThanConstraint[LengthUnit] +final case class LessThanAreaConstraint(value: AreaUnit, inclusive: Boolean) extends LessThanConstraint[AreaUnit] + +sealed abstract class MultipleOfConstraint[T <: Unit] extends Constrained[T] { + def value: T +} +final case class MultipleOfUnitlessConstraint(value: Unitless) extends MultipleOfConstraint[Unitless] +final case class MultipleOfLengthConstraint(value: LengthUnit) extends MultipleOfConstraint[LengthUnit] +final case class MultipleOfAreaConstraint(value: AreaUnit) extends MultipleOfConstraint[AreaUnit] + +// TODO do we have discrete combinations (e.g. BetweenConstraint) or do we provide a way to do unions? + +sealed abstract class NotConstraint[T <: Unit] { + def constraint: Constrained[T] +} +final case class NotUnitlessConstraint(constraint: Constrained[Unitless]) extends NotConstraint[Unitless] +final case class NotLengthConstraint(constraint: Constrained[LengthUnit]) extends NotConstraint[LengthUnit] +final case class NotAreaConstraint(constraint: Constrained[AreaUnit]) extends NotConstraint[AreaUnit] + +sealed abstract class AndConstraint[T <: Unit] { + def constraints: List[Constrained[T]] +} +final case class AndUnitlessConstraint(constraints: List[Constrained[Unitless]]) extends AndConstraint[Unitless] +final case class AndLengthConstraint(constraints: List[Constrained[LengthUnit]]) extends AndConstraint[LengthUnit] +final case class AndAreaConstraint(constraints: List[Constrained[AreaUnit]]) extends AndConstraint[AreaUnit] + +sealed abstract class OrConstraint[T <: Unit] { + def constraints: List[Constrained[T]] +} +final case class OrUnitlessConstraint(constraints: List[Constrained[Unitless]]) extends OrConstraint[Unitless] +final case class OrLengthConstraint(constraints: List[Constrained[LengthUnit]]) extends OrConstraint[LengthUnit] +final case class OrAreaConstraint(constraints: List[Constrained[AreaUnit]]) extends OrConstraint[AreaUnit] + diff --git a/floorplan/src/main/scala/IR.scala b/floorplan/src/main/scala/IR.scala new file mode 100644 index 00000000..79f22576 --- /dev/null +++ b/floorplan/src/main/scala/IR.scala @@ -0,0 +1,114 @@ +// See LICENSE for license details +package barstools.floorplan + +sealed abstract class IRLevel(val level: Int) { + // TODO +} + +case object IRLevel0 extends IRLevel(0) +case object IRLevel1 extends IRLevel(1) +case object IRLevel2 extends IRLevel(2) + + +////////////////////////////////////////////// Base classes + +sealed abstract class Element { + val name: String + + def level: IRLevel + def serialize = FloorplanSerialization.serialize(this) + +} + +sealed abstract class Primitive extends Element + +////////////////////////////////////////////// Rect shape + +sealed abstract class AbstractRect extends Primitive { + final def level = IRLevel2 +} + +sealed abstract class ConstrainedRect extends Primitive { + final def level = IRLevel1 + + val width: Constraint[LengthUnit] + val height: Constraint[LengthUnit] + val area: Constraint[AreaUnit] + val aspectRatio: Constraint[Unitless] + +} + +sealed abstract class ConcreteRect extends Primitive { + final def level = IRLevel0 + + val width: LengthUnit + val height: LengthUnit + +} + +sealed trait MacroLike + +final case class AbstractMacro(name: String) extends AbstractRect with MacroLike + +final case class ConcreteMacro(name: String, width: LengthUnit, height: LengthUnit) extends ConcreteRect with MacroLike + +sealed trait LogicLike { + val hardBoundary: Boolean +} + +final case class AbstractLogicRect(name: String, hardBoundary: Boolean) extends AbstractRect with LogicLike { + + def constrain( + width: Constraint[LengthUnit] = UnconstrainedLength, + height: Constraint[LengthUnit] = UnconstrainedLength, + area: Constraint[AreaUnit] = UnconstrainedArea, + aspectRatio: Constraint[Unitless] = UnconstrainedUnitless) = ConstrainedLogicRect(name, width, height, area, aspectRatio, hardBoundary) + +} + +final case class ConstrainedLogicRect( + name: String, + width: Constraint[LengthUnit], + height: Constraint[LengthUnit], + area: Constraint[AreaUnit], + aspectRatio: Constraint[Unitless], + hardBoundary: Boolean) extends ConstrainedRect with LogicLike + +final case class ConcreteLogicRect(name: String, width: LengthUnit, height: LengthUnit, hardBoundary: Boolean) extends ConcreteRect with LogicLike + +sealed abstract class Group extends Element + +sealed abstract class Grid extends Group { + val xDim: Int + val yDim: Int + val elements: Seq[Element] + + assert(xDim > 0, "X dimension of grid must be positive") + assert(yDim > 0, "Y dimension of grid must be positive") + + def get(x: Int, y: Int) = elements(xDim*y + x) +} + +final case class AbstractPackedHomogenousGrid[T <: Element](name: String, xDim: Int, yDim: Int, elements: Seq[T]) extends Grid { + def level = IRLevel2 +} + +object FloorplanSerialization { + + import org.json4s._ + import org.json4s.native.Serialization.{read, write} + import scala.reflect.runtime.universe.typeOf + + val formats = new DefaultFormats { + override val typeHintFieldName = "class" + override val typeHints = FullTypeHints(typeOf[Element]. + typeSymbol. + asClass. + knownDirectSubclasses. + toList. + map { x => Class.forName(x.fullName) }) + } + + def serialize[T <: Element](elt: T): String = write(elt)(formats) + +} diff --git a/floorplan/src/main/scala/Serialization.scala b/floorplan/src/main/scala/Serialization.scala new file mode 100644 index 00000000..31893b7e --- /dev/null +++ b/floorplan/src/main/scala/Serialization.scala @@ -0,0 +1,5 @@ +// See LICENSE for license details +// + +package barstools.floorplan + diff --git a/floorplan/src/main/scala/chisel/API.scala b/floorplan/src/main/scala/chisel/API.scala new file mode 100644 index 00000000..eb91a8d5 --- /dev/null +++ b/floorplan/src/main/scala/chisel/API.scala @@ -0,0 +1,4 @@ +// See LICENSE for license details +package barstools.floorplan + + diff --git a/floorplan/src/main/scala/chisel/ChiselAnnotations.scala b/floorplan/src/main/scala/chisel/ChiselAnnotations.scala new file mode 100644 index 00000000..67e12536 --- /dev/null +++ b/floorplan/src/main/scala/chisel/ChiselAnnotations.scala @@ -0,0 +1,15 @@ +// See LICENSE for license details +package barstools.floorplan.chisel + +import barstools.floorplan.firrtl.{FloorplanModuleAnnotation} +import barstools.floorplan.{Element} +import chisel3.experimental.{annotate, ChiselAnnotation, RawModule} + + +object Floorplan { + + def apply[T <: RawModule, U <: Element](m: T, fpElement: U): Unit = { + annotate(new ChiselAnnotation { def toFirrtl: FloorplanModuleAnnotation = FloorplanModuleAnnotation(m.toNamed.toTarget, fpElement.serialize) }) + } + +} diff --git a/floorplan/src/main/scala/firrtl/Annotations.scala b/floorplan/src/main/scala/firrtl/Annotations.scala new file mode 100644 index 00000000..40e9cbcd --- /dev/null +++ b/floorplan/src/main/scala/firrtl/Annotations.scala @@ -0,0 +1,19 @@ +// See LICENSE for license details +package barstools.floorplan.firrtl + + +import barstools.floorplan.{Element} +import firrtl.annotations.{NoTargetAnnotation, SingleTargetAnnotation, Target, ModuleTarget, InstanceTarget, ReferenceTarget} + +// John: Right now we are using ModuleTarget, which will mean that all instances of the same master +// will have the same floorplan. This is probably OK for now, but eventually we will want to support +// instance targets as well, which have some deduping issues. +// +// John: Another note. To make this a bit easier, I'm going to make floorplan IR embedded in this annotation rather than relying on +// the annotation to serialize the case class correctly (it doesn't currently serialize type parameters, which makes this a bit painful) +// We'll probably want to change this later +case class FloorplanModuleAnnotation(target: ModuleTarget, fpir: String) extends SingleTargetAnnotation[ModuleTarget] { + def duplicate(t: ModuleTarget) = this.copy(target, fpir) +} + + From 63b9e05171a025254aab40e101c4e79983ab41cf Mon Sep 17 00:00:00 2001 From: John Wright Date: Thu, 24 Oct 2019 11:35:20 -0700 Subject: [PATCH 02/73] This emits the right firrtl collateral, but barstools does not accept the custom command-line args correctly --- .../firrtl.options.RegisteredTransform | 1 + floorplan/src/main/scala/IR.scala | 20 +-------- floorplan/src/main/scala/Serialization.scala | 29 +++++++++++- .../main/scala/chisel/ChiselAnnotations.scala | 16 ++++++- .../src/main/scala/firrtl/Annotations.scala | 4 +- floorplan/src/main/scala/firrtl/Passes.scala | 45 +++++++++++++++++++ 6 files changed, 90 insertions(+), 25 deletions(-) create mode 100644 floorplan/src/main/resources/META-INF/services/firrtl.options.RegisteredTransform create mode 100644 floorplan/src/main/scala/firrtl/Passes.scala diff --git a/floorplan/src/main/resources/META-INF/services/firrtl.options.RegisteredTransform b/floorplan/src/main/resources/META-INF/services/firrtl.options.RegisteredTransform new file mode 100644 index 00000000..65d879eb --- /dev/null +++ b/floorplan/src/main/resources/META-INF/services/firrtl.options.RegisteredTransform @@ -0,0 +1 @@ +barstools.floorplan.firrtl.GenerateFloorplanIRPass diff --git a/floorplan/src/main/scala/IR.scala b/floorplan/src/main/scala/IR.scala index 79f22576..15950294 100644 --- a/floorplan/src/main/scala/IR.scala +++ b/floorplan/src/main/scala/IR.scala @@ -93,22 +93,4 @@ final case class AbstractPackedHomogenousGrid[T <: Element](name: String, xDim: def level = IRLevel2 } -object FloorplanSerialization { - - import org.json4s._ - import org.json4s.native.Serialization.{read, write} - import scala.reflect.runtime.universe.typeOf - - val formats = new DefaultFormats { - override val typeHintFieldName = "class" - override val typeHints = FullTypeHints(typeOf[Element]. - typeSymbol. - asClass. - knownDirectSubclasses. - toList. - map { x => Class.forName(x.fullName) }) - } - - def serialize[T <: Element](elt: T): String = write(elt)(formats) - -} +// TODO add more layouts here diff --git a/floorplan/src/main/scala/Serialization.scala b/floorplan/src/main/scala/Serialization.scala index 31893b7e..5549ff9b 100644 --- a/floorplan/src/main/scala/Serialization.scala +++ b/floorplan/src/main/scala/Serialization.scala @@ -1,5 +1,30 @@ // See LICENSE for license details -// - package barstools.floorplan +import org.json4s._ +import org.json4s.native.Serialization.{read, write} +import scala.reflect.runtime.universe.typeOf + +object FloorplanSerialization { + + // Because Element is sealed, all of its subclasses are known at compile time, so we can construct type hints for them + val formats = new DefaultFormats { + override val typeHintFieldName = "class" + override val typeHints = FullTypeHints(typeOf[Element]. + typeSymbol. + asClass. + knownDirectSubclasses. + toList. + map { x => Class.forName(x.fullName) }) + } + + def serialize[T <: Element](elt: T): String = write(elt)(formats) + def serialize[T <: Element](elts: Seq[(String, T)]): String = write(elts)(formats) + + def deserialize(fpir: String): Element = { + // Not sure why the implicit doesn't work at the original definition, but the compiler complains + implicit val formats = FloorplanSerialization.formats + read(fpir) + } + +} diff --git a/floorplan/src/main/scala/chisel/ChiselAnnotations.scala b/floorplan/src/main/scala/chisel/ChiselAnnotations.scala index 67e12536..ed1022cc 100644 --- a/floorplan/src/main/scala/chisel/ChiselAnnotations.scala +++ b/floorplan/src/main/scala/chisel/ChiselAnnotations.scala @@ -1,15 +1,27 @@ // See LICENSE for license details package barstools.floorplan.chisel -import barstools.floorplan.firrtl.{FloorplanModuleAnnotation} +import barstools.floorplan.firrtl.{FloorplanModuleAnnotation, GenerateFloorplanIRPass} import barstools.floorplan.{Element} import chisel3.experimental.{annotate, ChiselAnnotation, RawModule} +import firrtl.stage.RunFirrtlTransformAnnotation object Floorplan { - def apply[T <: RawModule, U <: Element](m: T, fpElement: U): Unit = { + private var annotatedPass = false + + def setLayout[T <: RawModule, U <: Element](m: T, fpElement: U): Unit = { annotate(new ChiselAnnotation { def toFirrtl: FloorplanModuleAnnotation = FloorplanModuleAnnotation(m.toNamed.toTarget, fpElement.serialize) }) + + // Only add the RunFirrtlTransformAnnotation once + if (!annotatedPass) { + annotate(new ChiselAnnotation { + def toFirrtl: RunFirrtlTransformAnnotation = new RunFirrtlTransformAnnotation(new GenerateFloorplanIRPass()) + }) + annotatedPass = true + } } } + diff --git a/floorplan/src/main/scala/firrtl/Annotations.scala b/floorplan/src/main/scala/firrtl/Annotations.scala index 40e9cbcd..18982d66 100644 --- a/floorplan/src/main/scala/firrtl/Annotations.scala +++ b/floorplan/src/main/scala/firrtl/Annotations.scala @@ -8,7 +8,7 @@ import firrtl.annotations.{NoTargetAnnotation, SingleTargetAnnotation, Target, M // John: Right now we are using ModuleTarget, which will mean that all instances of the same master // will have the same floorplan. This is probably OK for now, but eventually we will want to support // instance targets as well, which have some deduping issues. -// + // John: Another note. To make this a bit easier, I'm going to make floorplan IR embedded in this annotation rather than relying on // the annotation to serialize the case class correctly (it doesn't currently serialize type parameters, which makes this a bit painful) // We'll probably want to change this later @@ -16,4 +16,4 @@ case class FloorplanModuleAnnotation(target: ModuleTarget, fpir: String) extends def duplicate(t: ModuleTarget) = this.copy(target, fpir) } - +case class FloorplanIRFileAnnotation(value: String) extends NoTargetAnnotation diff --git a/floorplan/src/main/scala/firrtl/Passes.scala b/floorplan/src/main/scala/firrtl/Passes.scala new file mode 100644 index 00000000..7f1433be --- /dev/null +++ b/floorplan/src/main/scala/firrtl/Passes.scala @@ -0,0 +1,45 @@ +// See LICENSE for license details +package barstools.floorplan.firrtl + +import barstools.floorplan.FloorplanSerialization +import firrtl.{CircuitState, LowForm, Namespace, Transform, AnnotationSeq} +import firrtl.options.{RegisteredTransform, ShellOption} +import firrtl.analyses.{InstanceGraph} + +// NOTE: If you rename/add this transform, don't forget to update META-INF +// See the @note in the RegisteredTransform documentation +class GenerateFloorplanIRPass extends Transform with RegisteredTransform { + def inputForm = LowForm + def outputForm = LowForm + + val options = Seq( + new ShellOption[String]( + longOption = "floorplan-ir-file", + toAnnotationSeq = (a: String) => Seq(FloorplanIRFileAnnotation(a)), + helpText = s"Set the floorplan IR file name" + ) + ) + + def execute(state: CircuitState): CircuitState = { + state.annotations.collectFirst({ + case x: FloorplanIRFileAnnotation => x.value + }).headOption.map { filename => + val writer = new java.io.FileWriter(filename) + val graph = new InstanceGraph(state.circuit) + val list = state.annotations.collect { + case x: FloorplanModuleAnnotation => + graph.findInstancesInHierarchy(x.target.name). + map(_.tail.map(_.name)). + reduce(_ ++ _). + map((_, FloorplanSerialization.deserialize(x.fpir))) + } reduce (_ ++ _) + writer.write(FloorplanSerialization.serialize(list)) + writer.close() + } getOrElse { + val opt = options.head.longOption + throw new Exception(s"Did not specify a filename for GenerateFloorplanIRPass. Please provide a FloorplanIRFileAnnotation or use the --${opt} option.") + } + state + } +} + From 445564f9fdf7fd53d103241d465137fe8b3ef300 Mon Sep 17 00:00:00 2001 From: John Wright Date: Fri, 25 Oct 2019 13:06:45 -0700 Subject: [PATCH 03/73] Fix FPIR serialization --- floorplan/src/main/scala/Serialization.scala | 6 ++-- floorplan/src/main/scala/firrtl/Passes.scala | 37 ++++++++++++-------- 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/floorplan/src/main/scala/Serialization.scala b/floorplan/src/main/scala/Serialization.scala index 5549ff9b..e5c46a61 100644 --- a/floorplan/src/main/scala/Serialization.scala +++ b/floorplan/src/main/scala/Serialization.scala @@ -5,6 +5,8 @@ import org.json4s._ import org.json4s.native.Serialization.{read, write} import scala.reflect.runtime.universe.typeOf +final case class FloorplanElementRecord[T <: Element](path: String, elt: T) + object FloorplanSerialization { // Because Element is sealed, all of its subclasses are known at compile time, so we can construct type hints for them @@ -19,12 +21,12 @@ object FloorplanSerialization { } def serialize[T <: Element](elt: T): String = write(elt)(formats) - def serialize[T <: Element](elts: Seq[(String, T)]): String = write(elts)(formats) + def serialize[T <: Element](elts: Seq[FloorplanElementRecord[T]]): String = write(elts)(formats) def deserialize(fpir: String): Element = { // Not sure why the implicit doesn't work at the original definition, but the compiler complains implicit val formats = FloorplanSerialization.formats - read(fpir) + read[Element](fpir) } } diff --git a/floorplan/src/main/scala/firrtl/Passes.scala b/floorplan/src/main/scala/firrtl/Passes.scala index 7f1433be..1471da76 100644 --- a/floorplan/src/main/scala/firrtl/Passes.scala +++ b/floorplan/src/main/scala/firrtl/Passes.scala @@ -1,7 +1,7 @@ // See LICENSE for license details package barstools.floorplan.firrtl -import barstools.floorplan.FloorplanSerialization +import barstools.floorplan.{FloorplanSerialization, FloorplanElementRecord} import firrtl.{CircuitState, LowForm, Namespace, Transform, AnnotationSeq} import firrtl.options.{RegisteredTransform, ShellOption} import firrtl.analyses.{InstanceGraph} @@ -20,24 +20,31 @@ class GenerateFloorplanIRPass extends Transform with RegisteredTransform { ) ) + private def optSeq[T](s: Seq[T]): Option[Seq[T]] = { + // This works in scala 2.13 + //Option.when(s.nonEmpty)(s) + if (s.nonEmpty) Some(s) else None + } + def execute(state: CircuitState): CircuitState = { - state.annotations.collectFirst({ - case x: FloorplanIRFileAnnotation => x.value - }).headOption.map { filename => + // TODO don't need graph if there are no annos, which can be a speedup + val graph = new InstanceGraph(state.circuit) + optSeq(state.annotations.collect({ + case x: FloorplanModuleAnnotation => + graph.findInstancesInHierarchy(x.target.name). + map(_.tail.map(_.name)). + reduce(_ ++ _). + map(FloorplanElementRecord(_, FloorplanSerialization.deserialize(x.fpir))) + }).reduce(_ ++ _)).foreach { list => + val filename = state.annotations.collectFirst({ + case x: FloorplanIRFileAnnotation => x.value + }).getOrElse { + val opt = options.head.longOption + throw new Exception(s"Did not specify a filename for GenerateFloorplanIRPass. Please provide a FloorplanIRFileAnnotation or use the --${opt} option.") + } val writer = new java.io.FileWriter(filename) - val graph = new InstanceGraph(state.circuit) - val list = state.annotations.collect { - case x: FloorplanModuleAnnotation => - graph.findInstancesInHierarchy(x.target.name). - map(_.tail.map(_.name)). - reduce(_ ++ _). - map((_, FloorplanSerialization.deserialize(x.fpir))) - } reduce (_ ++ _) writer.write(FloorplanSerialization.serialize(list)) writer.close() - } getOrElse { - val opt = options.head.longOption - throw new Exception(s"Did not specify a filename for GenerateFloorplanIRPass. Please provide a FloorplanIRFileAnnotation or use the --${opt} option.") } state } From 95f147eaf125817bb1ac60f689c8f7d93fb19b71 Mon Sep 17 00:00:00 2001 From: John Wright Date: Tue, 29 Oct 2019 16:04:14 -0700 Subject: [PATCH 04/73] Add basic compiler App --- floorplan/src/main/scala/IR.scala | 26 +++++++-------- floorplan/src/main/scala/Serialization.scala | 32 +++++++++++++------ .../scala/compiler/FloorplanCompiler.scala | 32 +++++++++++++++++++ .../src/main/scala/compiler/Passes.scala | 11 +++++++ floorplan/src/main/scala/firrtl/Passes.scala | 4 +-- 5 files changed, 78 insertions(+), 27 deletions(-) create mode 100644 floorplan/src/main/scala/compiler/FloorplanCompiler.scala create mode 100644 floorplan/src/main/scala/compiler/Passes.scala diff --git a/floorplan/src/main/scala/IR.scala b/floorplan/src/main/scala/IR.scala index 15950294..97f818d6 100644 --- a/floorplan/src/main/scala/IR.scala +++ b/floorplan/src/main/scala/IR.scala @@ -1,21 +1,13 @@ // See LICENSE for license details package barstools.floorplan -sealed abstract class IRLevel(val level: Int) { - // TODO -} - -case object IRLevel0 extends IRLevel(0) -case object IRLevel1 extends IRLevel(1) -case object IRLevel2 extends IRLevel(2) - - ////////////////////////////////////////////// Base classes sealed abstract class Element { val name: String - def level: IRLevel + // TODO make this an Enumeration + def level: Int def serialize = FloorplanSerialization.serialize(this) } @@ -25,11 +17,11 @@ sealed abstract class Primitive extends Element ////////////////////////////////////////////// Rect shape sealed abstract class AbstractRect extends Primitive { - final def level = IRLevel2 + final def level = 2 } sealed abstract class ConstrainedRect extends Primitive { - final def level = IRLevel1 + final def level = 1 val width: Constraint[LengthUnit] val height: Constraint[LengthUnit] @@ -39,7 +31,7 @@ sealed abstract class ConstrainedRect extends Primitive { } sealed abstract class ConcreteRect extends Primitive { - final def level = IRLevel0 + final def level = 0 val width: LengthUnit val height: LengthUnit @@ -89,8 +81,12 @@ sealed abstract class Grid extends Group { def get(x: Int, y: Int) = elements(xDim*y + x) } -final case class AbstractPackedHomogenousGrid[T <: Element](name: String, xDim: Int, yDim: Int, elements: Seq[T]) extends Grid { - def level = IRLevel2 +final case class AbstractGrid[T <: Element](name: String, xDim: Int, yDim: Int, elements: Seq[T]) extends Grid { + def level = 2 +} + +final case class WeightedGrid[T <: Element](name: String, xDim: Int, yDim: Int, elements: Seq[T], weights: Seq[Int], packed: Boolean) extends Grid { + def level = 1 } // TODO add more layouts here diff --git a/floorplan/src/main/scala/Serialization.scala b/floorplan/src/main/scala/Serialization.scala index e5c46a61..8c78093d 100644 --- a/floorplan/src/main/scala/Serialization.scala +++ b/floorplan/src/main/scala/Serialization.scala @@ -5,28 +5,40 @@ import org.json4s._ import org.json4s.native.Serialization.{read, write} import scala.reflect.runtime.universe.typeOf -final case class FloorplanElementRecord[T <: Element](path: String, elt: T) +final case class FloorplanElementRecord[T <: Element](path: String, element: T) + +final case class FloorplanState(elements: Seq[FloorplanElementRecord[Element]], level: Int) object FloorplanSerialization { + private val elements = typeOf[Element].typeSymbol.asClass.knownDirectSubclasses.toList + // Because Element is sealed, all of its subclasses are known at compile time, so we can construct type hints for them val formats = new DefaultFormats { override val typeHintFieldName = "class" - override val typeHints = FullTypeHints(typeOf[Element]. - typeSymbol. - asClass. - knownDirectSubclasses. - toList. - map { x => Class.forName(x.fullName) }) + override val typeHints = FullTypeHints(elements map { x => Class.forName(x.fullName) }) } def serialize[T <: Element](elt: T): String = write(elt)(formats) - def serialize[T <: Element](elts: Seq[FloorplanElementRecord[T]]): String = write(elts)(formats) - def deserialize(fpir: String): Element = { + def deserialize(str: String): Element = { // Not sure why the implicit doesn't work at the original definition, but the compiler complains implicit val formats = FloorplanSerialization.formats - read[Element](fpir) + read[Element](str) + } + +} + +object FloorplanState { + + def fromSeq(seq: Seq[FloorplanElementRecord[Element]]): FloorplanState = FloorplanState(seq, seq.map(_.element.level).max) + + def serialize(state: FloorplanState): String = write(state)(FloorplanSerialization.formats) + def serialize(seq: Seq[FloorplanElementRecord[Element]]): String = serialize(fromSeq(seq)) + + def deserialize(str: String): FloorplanState = { + implicit val formats = FloorplanSerialization.formats + read[FloorplanState](str) } } diff --git a/floorplan/src/main/scala/compiler/FloorplanCompiler.scala b/floorplan/src/main/scala/compiler/FloorplanCompiler.scala new file mode 100644 index 00000000..28e73781 --- /dev/null +++ b/floorplan/src/main/scala/compiler/FloorplanCompiler.scala @@ -0,0 +1,32 @@ +// See LICENSE for license details +package barstools.floorplan.compiler + +import barstools.floorplan._ + +case class FloorplanOptions( + inputFloorplanFile: String = "", + outputFloorplanFile: String = "" +) + +object FloorplanCompiler extends App { + + val opts = (new scopt.OptionParser[FloorplanOptions]("fpCompiler") { + + opt[String]('i', "input-file"). + required(). + valueName(""). + action((x, c) => c.copy(inputFloorplanFile = x)). + text("input file name") + + opt[String]('o', "output-file"). + required(). + valueName(""). + action((x, c) => c.copy(outputFloorplanFile = x)). + text("output file name") + + }).parse(args, FloorplanOptions()).getOrElse { + throw new Exception("Error parsing options!") + } + + println(opts.inputFloorplanFile) +} diff --git a/floorplan/src/main/scala/compiler/Passes.scala b/floorplan/src/main/scala/compiler/Passes.scala new file mode 100644 index 00000000..c4b1330b --- /dev/null +++ b/floorplan/src/main/scala/compiler/Passes.scala @@ -0,0 +1,11 @@ +// See LICENSE for license details +package barstools.floorplan.compiler + +import barstools.floorplan.FloorplanState + +abstract class Pass { + + def execute(state: FloorplanState): FloorplanState + +} + diff --git a/floorplan/src/main/scala/firrtl/Passes.scala b/floorplan/src/main/scala/firrtl/Passes.scala index 1471da76..a27efd45 100644 --- a/floorplan/src/main/scala/firrtl/Passes.scala +++ b/floorplan/src/main/scala/firrtl/Passes.scala @@ -1,7 +1,7 @@ // See LICENSE for license details package barstools.floorplan.firrtl -import barstools.floorplan.{FloorplanSerialization, FloorplanElementRecord} +import barstools.floorplan.{FloorplanSerialization, FloorplanElementRecord, FloorplanState} import firrtl.{CircuitState, LowForm, Namespace, Transform, AnnotationSeq} import firrtl.options.{RegisteredTransform, ShellOption} import firrtl.analyses.{InstanceGraph} @@ -43,7 +43,7 @@ class GenerateFloorplanIRPass extends Transform with RegisteredTransform { throw new Exception(s"Did not specify a filename for GenerateFloorplanIRPass. Please provide a FloorplanIRFileAnnotation or use the --${opt} option.") } val writer = new java.io.FileWriter(filename) - writer.write(FloorplanSerialization.serialize(list)) + writer.write(FloorplanState.serialize(list)) writer.close() } state From d9e3deba8e106dce4640a252dee08715ade9925b Mon Sep 17 00:00:00 2001 From: Colin Schmidt Date: Thu, 31 Oct 2019 08:40:01 -0700 Subject: [PATCH 05/73] Updates for rocket-chip bump --- floorplan/src/main/scala/chisel/ChiselAnnotations.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/floorplan/src/main/scala/chisel/ChiselAnnotations.scala b/floorplan/src/main/scala/chisel/ChiselAnnotations.scala index ed1022cc..2496a841 100644 --- a/floorplan/src/main/scala/chisel/ChiselAnnotations.scala +++ b/floorplan/src/main/scala/chisel/ChiselAnnotations.scala @@ -3,7 +3,8 @@ package barstools.floorplan.chisel import barstools.floorplan.firrtl.{FloorplanModuleAnnotation, GenerateFloorplanIRPass} import barstools.floorplan.{Element} -import chisel3.experimental.{annotate, ChiselAnnotation, RawModule} +import chisel3.core.{RawModule} +import chisel3.experimental.{annotate, ChiselAnnotation} import firrtl.stage.RunFirrtlTransformAnnotation From 89114ffbf57bb5dc9e46022fe7063fdc828a9e9f Mon Sep 17 00:00:00 2001 From: Colin Schmidt Date: Thu, 31 Oct 2019 10:53:21 -0700 Subject: [PATCH 06/73] Move aoplib from fcl-floorplan --- aoplib/src/main/scala/aoplib/AOP.scala | 34 +++ .../src/main/scala/aoplib/LoggingAspect.scala | 158 +++++++++++ .../aoplib/breakpoint/BreakpointAspect.scala | 246 ++++++++++++++++++ .../scala/aoplib/coverage/CoverAspect.scala | 109 ++++++++ .../scala/aoplib/coverage/CoverGroup.scala | 85 ++++++ .../scala/aoplib/coverage/CoverPoint.scala | 90 +++++++ .../aoplib/coverage/CoverTransform.scala | 45 ++++ .../aoplib/coverage/CoverageTracker.scala | 139 ++++++++++ .../aoplib/custom/AnnotatingAspect.scala | 12 + .../aoplib/floorplan/FloorplanAspect.scala | 57 ++++ .../scala/aoplib/histogram/Histogram.scala | 91 +++++++ .../aoplib/histogram/HistogramAspect.scala | 89 +++++++ .../aoplib/histogram/HistogramSignal.scala | 32 +++ .../aoplib/redundancy/RedundancyAspect.scala | 152 +++++++++++ .../aoplib/redundancy/StuckFaultAspect.scala | 76 ++++++ 15 files changed, 1415 insertions(+) create mode 100644 aoplib/src/main/scala/aoplib/AOP.scala create mode 100644 aoplib/src/main/scala/aoplib/LoggingAspect.scala create mode 100644 aoplib/src/main/scala/aoplib/breakpoint/BreakpointAspect.scala create mode 100644 aoplib/src/main/scala/aoplib/coverage/CoverAspect.scala create mode 100644 aoplib/src/main/scala/aoplib/coverage/CoverGroup.scala create mode 100644 aoplib/src/main/scala/aoplib/coverage/CoverPoint.scala create mode 100644 aoplib/src/main/scala/aoplib/coverage/CoverTransform.scala create mode 100644 aoplib/src/main/scala/aoplib/coverage/CoverageTracker.scala create mode 100644 aoplib/src/main/scala/aoplib/custom/AnnotatingAspect.scala create mode 100644 aoplib/src/main/scala/aoplib/floorplan/FloorplanAspect.scala create mode 100644 aoplib/src/main/scala/aoplib/histogram/Histogram.scala create mode 100644 aoplib/src/main/scala/aoplib/histogram/HistogramAspect.scala create mode 100644 aoplib/src/main/scala/aoplib/histogram/HistogramSignal.scala create mode 100644 aoplib/src/main/scala/aoplib/redundancy/RedundancyAspect.scala create mode 100644 aoplib/src/main/scala/aoplib/redundancy/StuckFaultAspect.scala diff --git a/aoplib/src/main/scala/aoplib/AOP.scala b/aoplib/src/main/scala/aoplib/AOP.scala new file mode 100644 index 00000000..c27c68cd --- /dev/null +++ b/aoplib/src/main/scala/aoplib/AOP.scala @@ -0,0 +1,34 @@ +package aoplib + +import firrtl.RenameMap +import firrtl.annotations.ReferenceTarget + +object Main extends App { + println("Hello world.") +} + +object AOP { + def doStuff = println("Doing stuff") +} + +object AnnotationHelpers { + def renameOne(rt: ReferenceTarget, renames: RenameMap): ReferenceTarget = { + renames.get(rt) match { + case Some(s@Seq(x: ReferenceTarget)) => x + case None => rt + case x => sys.error(s"Cannot update $this when $rt is renamed to $x.") + } + } + def renameMany(rts: Seq[ReferenceTarget], renames: RenameMap): Seq[ReferenceTarget] = { + rts.flatMap { t => + renames.get(t) match { + case Some(seq) => seq.map { + case x: ReferenceTarget => x + case x => sys.error(s"Cannot update $this when $t is renamed to $x.") + } + case None => Seq(t) + } + } + } + +} \ No newline at end of file diff --git a/aoplib/src/main/scala/aoplib/LoggingAspect.scala b/aoplib/src/main/scala/aoplib/LoggingAspect.scala new file mode 100644 index 00000000..84e304f6 --- /dev/null +++ b/aoplib/src/main/scala/aoplib/LoggingAspect.scala @@ -0,0 +1,158 @@ +package aoplib + +import chisel3.{Bool, Data} +import firrtl.annotations.{Annotation, ReferenceTarget, Target} +import firrtl.{AnnotationSeq, CircuitForm, CircuitState, HighForm, IRToWorkingIR, LowForm, RenameMap, ResolveAndCheck, ResolvedAnnotationPaths, Transform, UNKNOWNGENDER, WRef, WSubField, WSubIndex} +import firrtl.ir._ +import firrtl.Mappers._ + +import scala.collection.mutable +import scala.reflect.runtime.universe.TypeTag + + + +//abstract class LoggingInfo(message: String, signals: Seq[Data], clock: chisel3.Clock, enable: Option[Bool] = None) extends AspectInfo { + + +//case class FirrtlLoggingInfo(enable: ReferenceTarget, signals: Seq[ReferenceTarget], clock: ReferenceTarget, message: String) { + +//abstract class LoggingAspect[T <: RawModule](implicit tag: TypeTag[T]) extends Aspect[T, LoggingInfo] { +// +// def logSignals(info: LoggingInfo): Unit +// +// override def collectAspectInfo(dut: T): Seq[LoggingInfo] = logSignals(dut) +// +// override def resolveAspectInfo(aspectInfo: Seq[LoggingInfo]): AnnotationSeq = { +// val fLogSignals = aspectInfo.map(i => FirrtlLoggingInfo(i.message, i.signals.map(_.toTarget), i.clock.toTarget, i.enable.map(_.toTarget))) +// Seq(LoggingSignals(fLogSignals)) +// } +// +// override def transformClass: Class[_ <: Transform] = classOf[LoggingTransform] +//} + + +// FIRRTL STUFF BELOW + +/* + +case class FirrtlLoggingInfo(message: String, signals: Seq[ReferenceTarget], clock: ReferenceTarget, enable: Option[ReferenceTarget]) { + private val circuits = getTargets.map(t => t.circuit).toSet + private val modules = getTargets.map(t => t.module).toSet + assert(circuits.size == 1 && modules.size == 1, s"All logging signals must share the same circuits $circuits and modules $modules: $this") + val circuit: String = circuits.head + val module: String = modules.head + val enRef: Option[String] = enable.map(_.ref) + val sigRefs: Seq[String] = signals.map(_.ref) + val clkRef: String = clock.ref + def getTargets: Seq[ReferenceTarget] = clock +: (signals ++ enable) +} + + +case class LoggingSignals(infos: Seq[FirrtlLoggingInfo]) extends Annotation with AnnotationHelpers { + override def getTargets: Seq[ReferenceTarget] = infos.flatMap(i => i.getTargets) + override def update(renames: RenameMap): Seq[Annotation] = { + val newTargets = infos.map{ i => + FirrtlLoggingInfo(i.message, renameMany(i.signals, renames), renameOne(i.clock, renames), i.enable.map(e => renameOne(e, renames))) + } + Seq(LoggingSignals(newTargets)) + } +} + +class LoggingTransform extends Transform with ResolvedAnnotationPaths { + override def inputForm: CircuitForm = HighForm + override def outputForm: CircuitForm = HighForm + + + override val annotationClasses: Traversable[Class[_]] = Seq(classOf[LoggingSignals]) + + override protected def execute(state: CircuitState): CircuitState = { + val infos = state.annotations.collect{ case a: LoggingSignals => a}.flatMap( a => a.infos ) + println("Executing Logging Transform on the following:") + + new ResolveAndCheck().runTransform(state.copy(circuit = addLogs(state.circuit, infos))) + } + + def addLogs(circuit: Circuit, infos: Seq[FirrtlLoggingInfo]): Circuit = { + val moduleSignalMap = mutable.HashMap[String, mutable.ArrayBuffer[FirrtlLoggingInfo]]() + infos.foreach { i => + i.getTargets.foreach { t => + assert(/*t.component == Nil &&*/ t.path == Nil) + } + moduleSignalMap.getOrElseUpdate(i.module, mutable.ArrayBuffer[FirrtlLoggingInfo]()) += i + } + def toExp(r: ReferenceTarget): Expression = { + def toWExp(e: Expression): Expression = e map toWExp match { + case Reference(name, _) => WRef(name) + case SubField(expr, name, _) => WSubField(expr, name) + case SubIndex(expr, idx, _) => WSubIndex(expr, idx, UnknownType, UNKNOWNGENDER) + } + toWExp(r.toNamed.expr) + } + + def addModuleLogs(logStuff: Seq[FirrtlLoggingInfo])(stmt: Statement): Statement = { + val prints: Seq[Print] = logStuff.map { i => + val enable = if(i.enable.isDefined) toExp(i.enable.get) else UIntLiteral(1) + val x = Print(NoInfo, StringLit(i.message), i.signals.map(toExp), toExp(i.clock), enable) + println(x.serialize) + x + } + Block(stmt +: prints) + } + + circuit map { m: DefModule => + if(moduleSignalMap.contains(m.name)) m map addModuleLogs(moduleSignalMap(m.name)) else m + } + } +} +*/ + +/* +object AspectModule { + private[aspect] val dynamicContextVar = new DynamicVariable[Option[MonitorModule]](None) + private[aspect] def withAspect[S](m: MonitorModule)(thunk: => S): S = { + dynamicContextVar.withValue(Some(m))(thunk) + } + def getFirrtl(chiselIR: chisel3.internal.firrtl.Circuit): firrtl.ir.DefModule = { + // Build FIRRTL AST + val firrtlString = chisel3.internal.firrtl.Emitter.emit(chiselIR) + val firrtlIR = firrtl.Parser.parse(firrtlString) + firrtlIR.modules.head + } + def getAnnotations(chiselIR: Circuit, dut: AspectModule, connections: Seq[(Component, Component)], parent: BaseModule): Seq[ChiselAnnotation] = { + + val firrtlModule = getFirrtl(chiselIR) + + // Return Annotations + Seq( + AspectAnnotation( + connections, + parent.toNamed, + //new AddInstance(dut.instName, firrtlModule.name), + _ => (s: Statement) => Block(Seq(s, DefInstance(NoInfo, dut.instName, firrtlModule.name))), + Seq(firrtlModule)) + ) ++ chiselIR.annotations + } + + def apply[M<: MultiIOModule, T<:Data](instanceName: String, + parent: M, + f: Snippet[M, T] + ): (MonitorModule, Seq[ChiselAnnotation]) = { + val connections = (parent: M, dut: MonitorModule) => { + dut.cmrComponent.toMap.mapValues(_()) ++ Map((parent.clock.toNamed, dut.instComponent.ref("clock")), (parent.reset.toNamed, dut.instComponent.ref("reset"))) + } + apply(instanceName, parent, () => new MonitorModule(instanceName, parent).snip(f), connections) + } + + def apply[M<: MultiIOModule, S<:AspectModule with RawModule](instanceName: String, + parent: M, + aspect: () => S, + connections: (M, S) => Map[Component, Component] + ): (S, Seq[ChiselAnnotation]) = { + // Elaborate aspect + val (chiselIR, dut) = Driver.elaborateAndReturn(aspect) + val connects = connections(parent, dut) + (dut, getAnnotations(chiselIR, dut, connects.toSeq, parent)) + } +} +*/ + diff --git a/aoplib/src/main/scala/aoplib/breakpoint/BreakpointAspect.scala b/aoplib/src/main/scala/aoplib/breakpoint/BreakpointAspect.scala new file mode 100644 index 00000000..455e8464 --- /dev/null +++ b/aoplib/src/main/scala/aoplib/breakpoint/BreakpointAspect.scala @@ -0,0 +1,246 @@ +package aoplib.breakpoint + +import java.io.File + +import aoplib.AnnotationHelpers +import chisel3._ +import chisel3.aop.Aspect +import chisel3.aop.injecting.{InjectingAspect, InjectingTransform} +import chisel3.experimental.{ChiselAnnotation, annotate} +import firrtl.Mappers._ +import _root_.firrtl.annotations.{Annotation, ModuleTarget, ReferenceTarget} +import firrtl.ir._ +import firrtl.stage.RunFirrtlTransformAnnotation +import firrtl.{AnnotationSeq, CircuitForm, CircuitState, HighForm, LowForm, MidForm, PrimOps, RenameMap, ResolveAndCheck, ResolvedAnnotationPaths, Transform, WRef, WSubField, WSubIndex} + +import scala.collection.mutable +import scala.io.Source +import scala.reflect.runtime.universe.TypeTag + +case class BreakpointAspect[T <: RawModule, M <: RawModule](selectInstances: T => Iterable[M], + setBreakCondition: M => Bool, + selectSignals: M => Seq[Data], + selectClockReset: M => (Clock, Reset), + srcPath: String + ) + (implicit tTag: TypeTag[T]) extends Aspect[T] { + def align(strings: Seq[String]): Seq[String] = { + strings.zip(getSpaces(strings.map(_.length))).map { + case (str, spaces) => str + spaces + } + } + + def getSpaces(lengths: Seq[Int]): Seq[String] = { + val maxLength = lengths.foldLeft(0){ (maxLength, len) => len.max(maxLength) } + lengths.map { len => " " * (maxLength - len) } + } + + def nDigits(width: Int): Int = { + Math.ceil(Math.log10(Math.pow(2, width) + 1)).toInt + } + + override def toAnnotation(top: T): AnnotationSeq = { + InjectingAspect[T, M]( + selectInstances, + { m: M => + val breakWhen = setBreakCondition(m) + dontTouch(breakWhen) + val nextCycle = RegInit(false.B) + nextCycle := breakWhen + when(nextCycle) { + stop() + } + val signals = selectSignals(m).map(_.toTarget) + val (clock, reset) = selectClockReset(m) + annotate(new ChiselAnnotation { + /** Conversion to FIRRTL Annotation */ + override def toFirrtl: Annotation = { + val path = new File(".").getAbsolutePath() + val x = Breakpoint(breakWhen.toTarget, clock.toTarget, reset.toTarget, signals, path + "/" + srcPath) + x + } + }) + } + ).toAnnotation(top) ++ Seq(RunFirrtlTransformAnnotation(new InjectingTransform), RunFirrtlTransformAnnotation(new BreakpointTransform)) + } +} + +case class Breakpoint(whenBreak: ReferenceTarget, clock: ReferenceTarget, reset: ReferenceTarget, signals: Seq[ReferenceTarget], file: String) extends Annotation { + def module = whenBreak.module + def circuit = whenBreak.module + override def update(renames: RenameMap): Seq[Annotation] = { + val newSignals = AnnotationHelpers.renameMany(signals, renames) + val newWhenBreak = AnnotationHelpers.renameOne(whenBreak, renames) + Seq(Breakpoint(newWhenBreak, clock, reset, newSignals, file)) + } +} + + +class BreakpointTransform extends Transform with ResolvedAnnotationPaths { + override val annotationClasses: Traversable[Class[_]] = Seq(classOf[Breakpoint]) + // Import utility functions from companion object + import Breakpoint._ + override def inputForm: CircuitForm = MidForm + override def outputForm: CircuitForm = HighForm + + override def execute(state: CircuitState): CircuitState = { + val moduleToBP = state.annotations.collect { case b: Breakpoint => b }.groupBy(b => b.module) + + val newMods = state.circuit.modules.map { m => + moduleToBP.get(m.name) match { + case None => m + case Some(breaks) => + breaks.foldLeft(m){ (mod, break) => + assert(break.getTargets.forall(_.asInstanceOf[ReferenceTarget].component == Nil)) + assert(break.getTargets.map(_.asInstanceOf[ReferenceTarget]).forall(_.module == break.whenBreak.module), this.toString) + + breakModule(break.whenBreak.ref, break.signals.map(_.ref), break.clock.ref, break.reset.ref, break.file)(m) + } + } + } + + val newState = state.copy(circuit = state.circuit.copy(modules = newMods)) + new ResolveAndCheck().execute(newState) + } + + + def breakModule(when: String, signals: Seq[String], clock: String, reset: String, path: String)(m: DefModule): DefModule = { + val lines = Source.fromFile(path).getLines + val filename = getFileName(path) + val connects = firrtl.passes.memlib.AnalysisUtils.getConnects(m) + + // Maps line number to (declaration, column number) + val infoMap = mutable.HashMap[Int, mutable.ArrayBuffer[(IsDeclaration, Int)]]() + + // Populates infoMap + def updateInfoMap(dec: IsDeclaration)(info: Info): Unit = { + info match { + case FileInfo(StringLit(i)) => i match { + case infoRegex(file, lineNumber, columnNumber) => + val value = infoMap.getOrElseUpdate(lineNumber.toInt, mutable.ArrayBuffer.empty[(IsDeclaration, Int)]) + value += ((dec, columnNumber.toInt)) + } + case MultiInfo(infos) => infos.foreach(updateInfoMap(dec)) + case NoInfo => + } + } + + val signalsToRecord = signals.flatMap { s => findDeps(connects)(s) } ++ signals + + // Walks statements to populate infoMap + def findInfo(s: Statement): Unit = { + s match { + case i: IsDeclaration if signalsToRecord.contains(i.name) => updateInfoMap(i)(i.info) + case other => + } + s foreachStmt findInfo + } + m.ports.foreach(p => updateInfoMap(p)(p.info)) + m foreachStmt findInfo + + // Now infoMap is filled in + + val annotatedScala = mutable.ArrayBuffer[Print]() + val notReset = DoPrim(PrimOps.Not,Seq(WRef(reset)), Nil, UnknownType) + lines.zipWithIndex.foreach { case (line, prevIndex) => + val index = prevIndex + 1 + if(infoMap.contains(index)) { + val indent = selectIndent(line) + val decs = infoMap(index).filter{ case (dec, _) => getFileName(dec.info) == filename} + if(decs.size == 1) { + val (dec, _) = decs.head + // TODO: Will not work with instances or memories, WRef(dec.name) + annotatedScala += Print(NoInfo, StringLit("%c[1;31m"),Seq(UIntLiteral(27)), WRef(clock), notReset) + annotatedScala += Print(NoInfo, StringLit(line), Nil, WRef(clock), notReset) + annotatedScala += Print(NoInfo, StringLit("%c[0m"),Seq(UIntLiteral(27)), WRef(clock), notReset) + + annotatedScala += Print(NoInfo, StringLit("%c[1;34m"),Seq(UIntLiteral(27)), WRef(clock), notReset) + annotatedScala += Print(NoInfo, StringLit(dec.name + ":%d\n"), Seq(WRef(dec.name)), WRef(clock), notReset) + annotatedScala += Print(NoInfo, StringLit("%c[0m"),Seq(UIntLiteral(27)), WRef(clock), notReset) + } else { + annotatedScala += Print(NoInfo, StringLit("%c[1;31m"),Seq(UIntLiteral(27)), WRef(clock), notReset) + annotatedScala += Print(NoInfo, StringLit(line + "\n"), Nil, WRef(clock), notReset) + annotatedScala += Print(NoInfo, StringLit("%c[0m"),Seq(UIntLiteral(27)), WRef(clock), notReset) + if(decs.size == 0) { + println("HERE") + } + decs.foreach { case (dec, column) => + annotatedScala += Print(NoInfo, StringLit("%c[1;34m"),Seq(UIntLiteral(27)), WRef(clock), notReset) + annotatedScala += Print( + NoInfo, + StringLit((" " * 0.max(column - dec.name.length)) + dec.name + ":%d\n"), + Seq(WRef(dec.name)), // TODO: Will not work with instances or memories + WRef(clock), + notReset + ) + annotatedScala += Print(NoInfo, StringLit("%c[0m"),Seq(UIntLiteral(27)), WRef(clock), notReset) + } + + } + } else { + annotatedScala += Print(NoInfo, StringLit(line + "\n"), Nil, WRef(clock), notReset) + } + + + } + + m match { + case m: firrtl.ir.Module => + m.copy(body = Block(Seq(m.body, Conditionally(NoInfo, WRef(when), Block(annotatedScala), EmptyStmt)))) + case other => other + } + } + +} + +object Breakpoint { + /** Matches a Firrtl info, e.g. @[ALU.scala 40:19] */ + val infoRegex = """([^ ]+) ([0-9]+):([0-9]+)""".r + def getFileName(path: String): String = path.split("/").last + def getFileName(info: Info): String = { + info match { + case FileInfo(StringLit(i)) => i match { + case infoRegex(file, line, co) => file + case other => "" + } + case other => "" + } + } + + def selectIndent(line: String): String = { + val (spacing, _) = line.foldLeft(("", true)){ case ((soFar, collecting), c) => + if(collecting) { + c match { + case ' ' => (soFar + c, collecting) + case '\t' => (soFar + c, collecting) + case other => (soFar, false) + } + } else (soFar, false) + } + spacing + } + + // Finds all signals who eventually drive name + def findDeps(connects: firrtl.passes.memlib.AnalysisUtils.Connects)(name: String): collection.Set[String] = { + val deps = mutable.HashSet[String]() + def getDeps(e: Expression): Expression = { + e match { + case WRef(name, _, _, _) => deps += name + case x: WSubField => deps += x.serialize + case x: WSubIndex => deps += x.serialize + case other => other map getDeps + } + e + } + + connects.get(name) match { + case None => Set.empty[String] + case Some(e) => getDeps(e) + } + deps ++ deps.flatMap { + case d if !deps.contains(d) => findDeps(connects)(d) + case d => Set(d) + } + } + +} diff --git a/aoplib/src/main/scala/aoplib/coverage/CoverAspect.scala b/aoplib/src/main/scala/aoplib/coverage/CoverAspect.scala new file mode 100644 index 00000000..c61a9e4c --- /dev/null +++ b/aoplib/src/main/scala/aoplib/coverage/CoverAspect.scala @@ -0,0 +1,109 @@ +package aoplib.coverage + +import aoplib.histogram.{HistogramAspect, HistogramSignal} +import chisel3.{Bits, Bool, Data} +import chisel3.aop.injecting.{InjectingAspect, InjectingTransform} +import chisel3.aop.{Aspect, Select} +import chisel3.core.RawModule +import chisel3.experimental.{ChiselAnnotation, annotate} +import chisel3.util.experimental.BoringUtils +import firrtl.{AnnotationSeq, RenameMap} +import firrtl.annotations.{Annotation, IsMember} +import firrtl.options.Unserializable +import firrtl.passes.wiring.WiringTransform +import firrtl.stage.RunFirrtlTransformAnnotation + +import scala.collection.MapLike +import scala.reflect.runtime.universe.TypeTag + + +trait CoverageOption + +case object SimulatorDone extends CoverageOption + +case class CoverageOptions(options: Map[CoverageOption, Any]) { + def simDone[T <: RawModule](top: T): Bool = options(SimulatorDone).asInstanceOf[T => Bool](top) +} + +case class CoverAspect[T <: RawModule](buildCoverage: T => Seq[CoverGroup], + coverage: CoverageOptions) + (implicit tTag: TypeTag[T]) extends Aspect[T] { + + override def toAnnotation(top: T): AnnotationSeq = { + val coverGroups = buildCoverage(top) + val groupMap = coverGroups.groupBy { group => group.module } + val annoSeqs = Select.collectDeep(top) { + case x: RawModule if groupMap.contains(x) => + val groups = groupMap(x).toList + assert(groups.size == 1, "Currently only support one covergroup per module") + + val ia = InjectingAspect[T, RawModule]( + (t: T) => Seq(x), + { m: RawModule => + import chisel3._ + val signals = groups.flatMap { group => + group.points.map { case CoverPoint(labelx, sig, bins, options) => + val defaults = bins.collect { case b@Bin(_, Default) => b } + assert(defaults.size <= 1, s"Coverpoint $labelx on signal ${sig.signal.toTarget} can no more than one default bin.") + assert(defaults.isEmpty, s"Don't support Default bin yet! Sorry :(") + val binIntervals = bins.filter { _.category.isInstanceOf[BinRange] }.sortBy { + case Bin(_, BinRange(low, high)) => low + } + new HistogramSignal(sig.signal) { + override def intervals: Seq[(Option[String], Int, Int)] = binIntervals.map{ + case Bin(n, BinRange(low, high)) => (Some(n), low.toInt, high.toInt) + } + override def label: Option[String] = Some(labelx) + } + } + } + val done = Wire(Bool()) + done := DontCare + BoringUtils.bore(coverage.simDone(top), Seq(done)) + signals.foreach { s => + val tracker = Module(new CoverageTracker(s, s.signal.name, m.name)) + tracker.in := s.signal + tracker.recording := true.B + tracker.printCoverage := done + } + } + ).toAnnotation(top) + + // Create annotation to insert histogram execution after design execution + //val ia2 = InjectingAspect({dut: T => Seq(dut)}, { dut: T => setDone(selectSimDone(dut)) }).toAnnotation(dut) + + //ia ++ ia2 ++ Seq(RunFirrtlTransformAnnotation(new InjectingTransform), RunFirrtlTransformAnnotation(new WiringTransform)) + ia + } + val ret = annoSeqs.toList.foldRight(Seq[Annotation](RunFirrtlTransformAnnotation(new WiringTransform))){ case (sofar, next) => next ++ sofar } + ret + } +} + +object SignalTracker { + def apply(signal: Bits, selector: Seq[IsMember] => Option[IsMember] = ts => Some(ts.head)): SignalTracker = { + SignalTracker(signal, Nil, selector, expanded = false) + } +} + +case class SignalTracker(signal: Bits, targets: Seq[IsMember], finalSelection: Seq[IsMember] => Option[IsMember], expanded: Boolean) extends Annotation with Unserializable { + def singleUpdate(renames: RenameMap): SignalTracker = { + val renamed = update(renames) + assert(renamed.size == 1, "Signal Tracker should always be renamed to a single other SignalTracker") + renamed.head + } + + override def update(renames: RenameMap): Seq[SignalTracker] = { + val expandedTargets = if(!expanded) { + assert(targets.isEmpty, "If SignalTracker isn't expanded, its targets should be empty.") + Seq(signal.toTarget) + } else targets + val newMembers = expandedTargets.flatMap { m: IsMember => + renames.get(m) match { + case Some(seq) => seq + case None => Seq(m) + } + } + if(!expanded) Seq(this.copy(targets = newMembers, expanded=true)) else Seq(this.copy(targets = newMembers)) + } +} diff --git a/aoplib/src/main/scala/aoplib/coverage/CoverGroup.scala b/aoplib/src/main/scala/aoplib/coverage/CoverGroup.scala new file mode 100644 index 00000000..cf2fc0e4 --- /dev/null +++ b/aoplib/src/main/scala/aoplib/coverage/CoverGroup.scala @@ -0,0 +1,85 @@ +package aoplib.coverage + +import aoplib.AnnotationHelpers +import chisel3.core.RawModule +import chisel3.aop.Aspect +import chisel3.aop.injecting.{InjectStatement, InjectingAspect, InjectingTransform} +import chisel3.{Clock, Reset} +import firrtl.annotations.Annotation +import firrtl.options.Unserializable +import firrtl.{AnnotationSeq, RenameMap} + +import scala.reflect.runtime.universe.TypeTag + +//object CoverGroup { +// def apply(label: String, +// module: RawModule, +// clock: Clock, +// reset: Reset, +// points: Seq[CoverPoint], +// options: GroupOptions = GroupOptions()): CoverGroup = { +// CoverGroup(label, module, clock, reset, points, options) +// } +//} + +case class CoverGroup (label: String, + module: RawModule, + clock: Clock, + reset: Reset, + points: Seq[CoverPoint], + options: GroupOptions = GroupOptions(), + ) extends Annotation with Unserializable { + override def update(renames: RenameMap): Seq[CoverGroup] = { + def updateTracker(t: SignalTracker): SignalTracker = { + val renamed = t.update(renames) + renamed.head + } + Seq(this.copy(points = points.map(_.update(renames)))) + } +} + +case class GroupOptions(weight: Int = 1) + +/* +weight=number, 1 + +If set at the covergroup syntactic level, it specifies the weight of this covergroup instance for computing the overalla + instance coverage of the simulation. +If set at the coverpoint (or cross) syntactic level, it specifies the weight of a coverpoint (or cross) for computing + the instance coverage of the enclosing covergroup. + +goal=number, 90 + +Specifies the target goal for a covergroup instance or for a coverpoint or a cross of an instance. + +name=string, unique name + +Specifies a name for the covergroup instance. + +comment=string + +A comment that appears with a covergroup instance or with a coverpoint or cross of the covergroup instance + +at_least=number, 1 + +Minimum number of times a bin needs to hit before it is declared as hit + +detect_overlap=boolean, 0 + +When true, a warning is issued if there is an overlap between the range list (or transition list) of two bins of a +coverpoint. + +auto_bin_max=number, 64 + +Maximum number of automatically created bins when no bins are explicitly defined for a coverpoint. + +cross_num_print_missing = number, 0 + +Number of missing (not covered) cross product bins that must be saved to the coverage database and printed in the +coverage report. + +per_instance=boolean, 0 + +Each instance contributes to the overall coverage information for the covergroup type. When true, coverage information +for this covergroup instance is tracked as well. + */ diff --git a/aoplib/src/main/scala/aoplib/coverage/CoverPoint.scala b/aoplib/src/main/scala/aoplib/coverage/CoverPoint.scala new file mode 100644 index 00000000..7ad6c4e0 --- /dev/null +++ b/aoplib/src/main/scala/aoplib/coverage/CoverPoint.scala @@ -0,0 +1,90 @@ +package aoplib.coverage + +import chisel3._ +import firrtl.RenameMap + +//trait CoverBase { val label: String } + + +object CoverPoint { + def apply(label: String, signal: Bits, bins: Seq[BaseBin], pointOptions: CoverOptions = CoverOptions()): CoverPoint = { + CoverPoint(label, SignalTracker(signal), bins, pointOptions) + } +} +case class CoverPoint private (label: String, + signal: SignalTracker, + bins: Seq[BaseBin], + pointOptions: CoverOptions) { + def update(renames: RenameMap): CoverPoint = { + this.copy(signal = signal.singleUpdate(renames)) + } + + /* + def generateChisel(clock: Clock, reset: Reset): Unit = { + val sig = signal.signal.asUInt() + withClockAndReset(clock, reset) { + val defaults = bins.collect { case b@Bin(_, Default) => b } + assert(defaults.size <= 1, s"Coverpoint $label on signal ${signal.signal.toTarget} can no more than one default bin.") + val inRanges = bins.map { + case Bin(label, BinRange(low, high)) => + val (counter, willWrap) = util.Counter(sig >= low.U & sig <= high.U, pointOptions.maxCount) + counter.suggestName(label) + when(willWrap) { + counter := counter + } + } + + } + + } + */ +} +case class CoverOptions(weights: Seq[Int] = Seq(1), maxCount: Int = 32) + +// TODO: I think you can get away without representing this directly and generating it programmatically +// case class CrossPoint(name: String, points: Seq[CoverPoint], bins: Seq[BaseBin]) extends CoverBase + +abstract class BaseBin { + val labelOption: Option[String] + val category: BinCategory +} + +// Explicit bin, bins based on category +case class Bin(label: String, category: BinCategory) extends BaseBin { + override val labelOption: Option[String] = Some(label) +} + +// Implicit bin, bins based on category +// Not user created +case class ImplicitBin(category: BinCategory) extends BaseBin { + override val labelOption: Option[String] = None +} + +// Ignores when bin matches (usually paired with ImplicitBin +case class IgnoreBin(label: String, category: BinCategory) extends BaseBin { + override val labelOption: Option[String] = Some(label) +} + + +trait BinCategory + +// Defaults to all non-specified categories +case object Default extends BinCategory + +// Low and High are inclusive +case class BinRange(low: BigInt, high: BigInt) extends BinCategory + +// A sequence of values that must be transitioned to, in order +// Wait on this... +//case class BinTransition(sequence: Seq[BinValue]) extends BinCategory + +// Unnecessary! +//trait BinValue + +// A value in a sequence that must match immediately +//case class BinConstant(value: BigInt) extends BinValue + +// A value that must be hit eventually, but not necessarily at this time +//case class BinEventually(value: BigInt) extends BinValue + + diff --git a/aoplib/src/main/scala/aoplib/coverage/CoverTransform.scala b/aoplib/src/main/scala/aoplib/coverage/CoverTransform.scala new file mode 100644 index 00000000..43c9069c --- /dev/null +++ b/aoplib/src/main/scala/aoplib/coverage/CoverTransform.scala @@ -0,0 +1,45 @@ +/* +package aoplib.coverage + +import firrtl.annotations.{Annotation, ReferenceTarget, SingleTargetAnnotation} +import firrtl.{CircuitForm, CircuitState, LowForm, ResolvedAnnotationPaths, Transform} + +case class SimulationFinished(target: ReferenceTarget) extends SingleTargetAnnotation[ReferenceTarget] { + override def duplicate(n: ReferenceTarget): Annotation = SimulationFinished(n) +} + + +class CoverTransform extends Transform with ResolvedAnnotationPaths { + override def inputForm: CircuitForm = LowForm + override def outputForm: CircuitForm = LowForm + + override val annotationClasses: Traversable[Class[_]] = List(classOf[SimulationFinished]) + + override def execute(state: CircuitState): CircuitState = { + val simFinishedSeq = state.annotations.collect { + case c: SimulationFinished => c + } + + assert(simFinishedSeq.size == simFinishedSeq.toSet.size, + s"There can only be one finished simulation signal, found: ${simFinishedSeq.map(_.target.serialize)}") + + val done = simFinishedSeq.toSet.head.target + + val circuit = state.circuit + done match { + case ReferenceTarget(cir, mod, Nil, ref, Nil) => + assert(circuit.main == cir, s"Simulation Signal circuit does not match given circuit: $cir != ${circuit.main}") + assert(circuit.modules.map(_.name).contains(mod), s"Simulation Signal module $mod is not found in circuit $cir") + val module = circuit.modules.collectFirst{ + case m if m.name == mod => m + }.get + + + case other => sys.error(s"Malformed simulation finished signal target: $other") + } + + state + } + +} +*/ diff --git a/aoplib/src/main/scala/aoplib/coverage/CoverageTracker.scala b/aoplib/src/main/scala/aoplib/coverage/CoverageTracker.scala new file mode 100644 index 00000000..0129961f --- /dev/null +++ b/aoplib/src/main/scala/aoplib/coverage/CoverageTracker.scala @@ -0,0 +1,139 @@ +package aoplib.coverage + +import aoplib.histogram.HistogramSignal +import chisel3._ + +/** Either records and bins the value of [in] every cycle, or cycles through all bins and prints the result + * + * @param hinfo contains signal (and its type) as well as other histogramming information + * @param name name of the instance of the parent module containing the signal + * @param module name of the parent module containing the signal + */ +class CoverageTracker(hinfo: HistogramSignal, name: String, module: String) extends MultiIOModule { + val in = IO(Input(chiselTypeOf(hinfo.signal))) + val recording = IO(Input(Bool())) + val printCoverage = IO(Input(Bool())) + + val inU = in.asUInt() + + val lows = VecInit(hinfo.intervals.map(_._2.U)) + val highs = VecInit(hinfo.intervals.map(_._3.U)) + + // Calculate in's address into histogram + val activeBinAddress = hinfo.intervals.zipWithIndex.foldLeft(0.U) { + case (addr: UInt, ((_, min: Int, max: Int), index: Int)) => Mux((inU >= min.U) & (inU < max.U), index.U, addr) + } + + // Instantiate coverage bins memory + val coverbins = Mem(math.pow(2, activeBinAddress.getWidth).toInt, chiselTypeOf(hinfo.maxCount.U)) + + // Records which bins have been written to (and which require initialization) + val hasWritten = RegInit(VecInit(Seq.fill(coverbins.length.toInt)(false.B))) + + // Represents the address of the current bin our input signal is falling into + val activeBinValue = Wire(chiselTypeOf(hinfo.maxCount.U)) + when(hasWritten(activeBinAddress)) { + activeBinValue := coverbins.read(activeBinAddress) + }.otherwise { + activeBinValue := 0.U + } + + // Then, do stuff + when(reset.asBool() === false.B) { + when(recording) { + val writeValue = (activeBinValue + 1.U).min(hinfo.maxCount.U) + coverbins.write(activeBinAddress, writeValue) + hasWritten(activeBinAddress) := true.B + } + + when(printCoverage) { + val (message, expsAll) = hinfo.intervals.zipWithIndex.foldLeft((s"Coverage of $name in module $module:\n", Seq.empty[Bits])) { + case ((str, exps), ((nameOpt, lo, hi), index)) => + /* + val start = if (hinfo.label.isDefined) { + s" ${hinfo.label.get}:" + } else s" $index:" + */ + val start = " " + val (strRest, expsRest) = if (nameOpt.isDefined) { + (start + s"Bin ${nameOpt.get} ($lo to $hi) -> %d\n", Seq(coverbins.read(index.U))) + } else { + (start + s"Bin $index ($lo to $hi) -> %d\n", Seq(coverbins.read(index.U))) + } + (str + strRest, exps ++ expsRest) + } + printf(message, expsAll:_*) + } + } +} + +/* + +covergroup address_cov (ref logic [7:0] address, + 22 input int low, int high) @ (posedge ce); + 23 ADDRESS : coverpoint address { + 24 bins low = {0,low}; + 25 bins med = {low,high}; + 26 } + 27 endgroup + 28 //================================================= + 29 // Instance of covergroup + 30 //================================================= + 31 address_cov acov_low = new(addr,0,10); + 32 address_cov acov_med = new(addr,11,20); + 33 address_cov acov_high = new(addr,21,30); + + =========================================================== + Group : coverage_covergroup.miff::address_cov + =========================================================== + SCORE WEIGHT GOAL + 100.00 1 100 + ----------------------------------------------------------- + Summary for Group coverage_covergroup.miff::address_cov + CATEGORY EXPECTED UNCOVERED COVERED PERCENT + Variables 2 0 2 100.00 + + Variables for Group coverage_covergroup.miff::address_cov + + VARIABLE EXPECTED UNCOVERED COVERED PERCENT GOAL WEIGHT + ADDRESS 2 0 2 100.00 100 1 + ----------------------------------------------------------- + Summary for Variable ADDRESS + + CATEGORY EXPECTED UNCOVERED COVERED PERCENT + User Defined Bins 2 0 2 100.00 + + User Defined Bins for ADDRESS + Bins + + NAME COUNT AT LEAST + med 2 1 + low 6 1 + */ + +/* +covergroup datac @ (negedge cif.cb.ce); + 83 data_in : coverpoint cif.cb.datai { + 84 bins low = {0,50}; + 85 bins med = {51,150}; + 86 bins high = {151,255}; + 87 } + 88 data_out : coverpoint cif.cb.datao { + 89 bins low = {0,50}; + 90 bins med = {51,150}; + 91 bins high = {151,255}; + 92 } + 93 read_write : coverpoint cif.cb.we { + 94 bins read = {0}; + 95 bins write = {1}; + 96 } + 97 endgroup + + VARIABLE EXPECTED UNCOVERED COVERED PERCENT GOAL WEIGHT + address 3 2 1 33.33 100 1 + + VARIABLE EXPECTED UNCOVERED COVERED PERCENT GOAL WEIGHT + data_in 3 2 1 33.33 100 1 + data_out 3 3 0 0.00 100 1 + read_write 2 0 2 100.00 100 1 + */ diff --git a/aoplib/src/main/scala/aoplib/custom/AnnotatingAspect.scala b/aoplib/src/main/scala/aoplib/custom/AnnotatingAspect.scala new file mode 100644 index 00000000..b5fe9319 --- /dev/null +++ b/aoplib/src/main/scala/aoplib/custom/AnnotatingAspect.scala @@ -0,0 +1,12 @@ +package aoplib.custom + +import chisel3.aop.Aspect +import chisel3.core.RawModule +import firrtl.{AnnotationSeq, Transform} + +import scala.reflect.runtime.universe.TypeTag + +case class AnnotatingAspect[T <: RawModule](annotateSignals: T => AnnotationSeq) + (implicit tTag: TypeTag[T]) extends Aspect[T] { + override def toAnnotation(top: T): AnnotationSeq = annotateSignals(top) +} diff --git a/aoplib/src/main/scala/aoplib/floorplan/FloorplanAspect.scala b/aoplib/src/main/scala/aoplib/floorplan/FloorplanAspect.scala new file mode 100644 index 00000000..33f42081 --- /dev/null +++ b/aoplib/src/main/scala/aoplib/floorplan/FloorplanAspect.scala @@ -0,0 +1,57 @@ +package aoplib.floorplan +import chisel3._ +import chisel3.aop.Aspect +import chisel3.core.Reset +import chisel3.experimental.{RunFirrtlTransform} +import firrtl.annotations._ +import firrtl.options.Unserializable +import firrtl.stage.RunFirrtlTransformAnnotation +import firrtl.{AnnotationSeq, CircuitForm, CircuitState, LowForm, MidForm, RenameMap, Transform} + +import scala.reflect.runtime.universe.TypeTag + +case class MemberTracker(name: String, targets: Seq[IsMember], finalSelection: Seq[IsMember] => Option[IsMember]) extends Annotation with Unserializable { + override def update(renames: RenameMap): Seq[Annotation] = { + val newMembers = targets.flatMap { m: IsMember => + renames.get(m) match { + case Some(seq) => seq + case None => Seq(m) + } + } + Seq(this.copy(targets = newMembers)) + } +} + +/* +case class FloorplanAspect[T <: RawModule](name: String, dir: String, buildFloorplan: T => LayoutBase) + (implicit tTag: TypeTag[T]) extends Aspect[T] { + def collectTrackers(layout: LayoutBase): Seq[MemberTracker] = { + def visit(layout: LayoutBase): Seq[MemberTracker] = { + val trackers = if(layout.properties.get(TargetKey).nonEmpty) { + val (target, finalMapping) = layout.get(TargetKey).asInstanceOf[(IsMember, Seq[IsMember] => Option[IsMember])] + Seq(MemberTracker(layout.name, Seq(target), finalMapping)) + } else Nil + layout match { + case arr: ArrayLayout => trackers ++ arr.elements.flatMap(visit) + case other => trackers + } + } + visit(layout) + } + override def toAnnotation(top: T): AnnotationSeq = { + val layout = buildFloorplan(top) + val trackers = collectTrackers(layout) + Seq(FloorplanInfo(layout, dir, name), RunFirrtlTransformAnnotation(new FloorplanTransform())) ++ trackers + } +} +*/ + +case class FloorplanAspectNew[T <: RawModule](name: String, dir: String, buildFloorplan: T => AnnotationSeq) + (implicit tTag: TypeTag[T]) extends Aspect[T] { + override def toAnnotation(top: T): AnnotationSeq = { + buildFloorplan(top) ++ Seq( + RunFirrtlTransformAnnotation(new barstools.floorplan.firrtl.GenerateFloorplanIRPass()), + barstools.floorplan.firrtl.FloorplanIRFileAnnotation("floorplan.ir") + ) + } +} diff --git a/aoplib/src/main/scala/aoplib/histogram/Histogram.scala b/aoplib/src/main/scala/aoplib/histogram/Histogram.scala new file mode 100644 index 00000000..cbd325ba --- /dev/null +++ b/aoplib/src/main/scala/aoplib/histogram/Histogram.scala @@ -0,0 +1,91 @@ +package aoplib.histogram + +import chisel3._ + +/** Either records and bins the value of [in] every cycle, or cycles through all bins and prints the result + * + * @param hinfo contains signal (and its type) as well as other histogramming information + * @param name name of the instance of the parent module containing the signal + * @param module name of the parent module containing the signal + */ +class Histogram(hinfo: HistogramSignal, name: String, module: String) extends MultiIOModule { + val in = IO(Input(chiselTypeOf(hinfo.signal))) + val setHistogramming = IO(Input(Bool())) + val setReadingOut = IO(Input(Bool())) + val doneReading = IO(Output(Bool())) + val allDone = IO(Input(Bool())) + doneReading := false.B + + assert((setHistogramming === false.B) | (setReadingOut === false.B), "Inputs setHistogramming and setReadingOut cannot both be true") + + val inU = in.asUInt() + // Calculate in's address into histogram + val inAddr = hinfo.intervals.zipWithIndex.foldLeft(0.U) { + case (addr: UInt, ((_, min: Int, max: Int), index: Int)) => Mux((inU >= min.U) & (inU < max.U), index.U, addr) + } + val lows = VecInit(hinfo.intervals.map(_._2.U)) + val highs = VecInit(hinfo.intervals.map(_._3.U)) + + // Instantiate histogram mem, actual read address (could change if not in histogramming mode), and readPort + val histMem = Mem(math.pow(2, inAddr.getWidth).toInt, chiselTypeOf(hinfo.maxCount.U)) + val readAddress = Wire(chiselTypeOf(inAddr.asUInt)) + readAddress := 0.U + val readPort = histMem.read(readAddress) + + // Calculate Read Value of input + val readValue = Wire(chiselTypeOf(readPort)) + val hasWritten = RegInit(VecInit(Seq.fill(histMem.length.toInt)(false.B))) + when(hasWritten(readAddress)) { + readValue := readPort + }.otherwise { + readValue := 0.U + } + + // Update histogram, or read out histogram + // First, remember previous state of setReadingOut + val pastSetReadingOut = RegNext(setReadingOut) + + // Then, do stuff + when(reset.asBool() === false.B) { + val readOutCounter = RegInit(chiselTypeOf(readAddress), 0.U) + when(setHistogramming) { + readAddress := inAddr.asUInt + val writeValue = (readPort + 1.U).min(hinfo.maxCount.U) + histMem.write(inAddr.asUInt(), writeValue) + hasWritten(inAddr.asUInt()) := true.B + } + + when(setReadingOut) { + readAddress := readOutCounter + when(pastSetReadingOut === false.B) { + // First cycle we are reading out the histogram + printf(s"Histogram for signal $name in module $module.\n") + } + val prevAddress = RegNext(readAddress) + when(readAddress < (lows.size - 1).U & (readAddress >= prevAddress)) { + readOutCounter := readOutCounter + 1.U + //printf(s"Bin %d (%d until %d) -> %d\n", readAddress, ticks(readAddress), ticks(readAddress +& 1.U), readPort) + }.otherwise { + doneReading := true.B + } + hinfo.intervals.zipWithIndex.foreach { case ((nameOpt, lo, hi), index) => + when(readAddress === index.U) { + val start = if(hinfo.label.isDefined) { + s"Histogram ${hinfo.label.get}" + } else s"Histogram $index" + + if(nameOpt.isDefined) { + printf(start + s" Bin ${nameOpt.get} (%d until %d) -> %d\n", lows(readAddress), highs(readAddress +& 1.U), readPort) + } else { + printf(start + s" Bin %d (%d until %d) -> %d\n", readAddress, lows(readAddress), highs(readAddress +& 1.U), readPort) + } + } + } + } + + when(allDone) { + doneReading := true.B + } + + } +} diff --git a/aoplib/src/main/scala/aoplib/histogram/HistogramAspect.scala b/aoplib/src/main/scala/aoplib/histogram/HistogramAspect.scala new file mode 100644 index 00000000..8e83991e --- /dev/null +++ b/aoplib/src/main/scala/aoplib/histogram/HistogramAspect.scala @@ -0,0 +1,89 @@ +package aoplib.histogram + +import chisel3._ +import chisel3.aop._ +import chisel3.aop.injecting.{InjectingAspect, InjectingTransform} +import chisel3.experimental.{ChiselAnnotation, annotate} +import chisel3.util.experimental.BoringUtils +import firrtl.annotations.Annotation +import firrtl.passes.wiring.WiringTransform +import firrtl.stage.RunFirrtlTransformAnnotation +import firrtl.{AnnotationSeq, Transform} + +import scala.reflect.runtime.universe.TypeTag + +/** Create histograms of signal values during execution, and print the bin values at the end of execution + * + * @param selectRoots Given top-level module, pick the instances of a module to apply the aspect (root module) + * @param selectSignals Pick signals from a module to histogram + * @param selectDesignDone From the top-level design, select a signal which, when true, the design has finished execution + * @param selectSimDone From the top-level design, select an assignable signal which, when true, the simulation has finished execution + * @param tTag Needed to prevent type-erasure of the top-level module type + * @param mTag Needed to prevent type-erasure of the selected modules' type + * @tparam T Type of top-level module + * @tparam M Type of root module (join point) + */ +case class HistogramAspect[T <: RawModule, M <: RawModule](selectRoots: T => Seq[M], + selectSignals: M => Seq[HistogramSignal], + selectDesignDone: T => Bool, + selectSimDone: T => Bool) + (implicit tTag: TypeTag[T], mTag: TypeTag[M]) extends Aspect[T] { + private final def markDone(d: Data): Unit = { + annotate(new ChiselAnnotation { + override def toFirrtl: Annotation = firrtl.passes.wiring.SourceAnnotation(d.toTarget, "histogramDone") + }) + } + + private final def setDone(d: Data): Unit = { + annotate(new ChiselAnnotation { + override def toFirrtl: Annotation = firrtl.passes.wiring.SinkAnnotation(d.toNamed, "histogramDone") + }) + } + + final def toAnnotation(dut: T): AnnotationSeq = { + // Create annotation to insert histogram into module + val ia = InjectingAspect[T, M]( + selectRoots, + { m: M => + val signals = selectSignals(m) + val done = Wire(Bool()) + done := DontCare + BoringUtils.bore(selectDesignDone(dut), Seq(done)) + val histograms = signals.map { s => + val histogram = Module(new Histogram(s, s.signal.name, m.name)) + histogram.in := s.signal + histogram.setHistogramming := true.B + histogram.setReadingOut := false.B + histogram.allDone := false.B + histogram + } + + val allDone = WireInit(false.B) + + when(done) { + histograms.foreach { h => + h.setHistogramming := false.B + } + allDone := histograms.foldLeft(1.U) { (readOut, h) => + h.setReadingOut := readOut + val regNext = RegNext(h.doneReading) + when(regNext) { + h.setReadingOut := false.B + h.allDone := true.B + } + + readOut & h.doneReading + } + } + + dontTouch(allDone) + markDone(allDone) + } + ).toAnnotation(dut) + + // Create annotation to insert histogram execution after design execution + val ia2 = InjectingAspect({dut: T => Seq(dut)}, { dut: T => setDone(selectSimDone(dut)) }).toAnnotation(dut) + + ia ++ ia2 ++ Seq(RunFirrtlTransformAnnotation(new InjectingTransform), RunFirrtlTransformAnnotation(new WiringTransform)) + } +} diff --git a/aoplib/src/main/scala/aoplib/histogram/HistogramSignal.scala b/aoplib/src/main/scala/aoplib/histogram/HistogramSignal.scala new file mode 100644 index 00000000..5c7fa644 --- /dev/null +++ b/aoplib/src/main/scala/aoplib/histogram/HistogramSignal.scala @@ -0,0 +1,32 @@ +package aoplib.histogram + +import chisel3._ + +/** Specifies signal whose values will be histogrammed after execution + * + * Can overwrite functions to customize the histogram behavior + * + * @param signal Signal to histogram + */ +class HistogramSignal(val signal: Bits) { + def maxCount = 100 + def minValue: Int = signal match { + case _: UInt => 0 + case s: SInt => Math.pow(2, s.getWidth - 1).toInt + } + /* Until max is the smallest illegal value, or the max legal value plus 1 */ + def untilMax: Int = signal match { + case u: UInt => Math.pow(2, u.getWidth).toInt + case s: SInt => Math.pow(2, s.getWidth - 1).toInt + } + def nBins: Int = untilMax - minValue + def ticks: Seq[Int] = { + val binInterval = (untilMax - minValue) / nBins + assert(binInterval * nBins + minValue == untilMax, + s"nBins ${nBins} must divide evenly into the range from ${minValue} until ${untilMax}") + val range = Range(minValue, untilMax + 1, binInterval) + range.toList + } + def intervals: Seq[(Option[String], Int, Int)] = ticks.zip(ticks.tail).map { case (lo, hi) => (None, lo, hi) } + def label: Option[String] = None +} diff --git a/aoplib/src/main/scala/aoplib/redundancy/RedundancyAspect.scala b/aoplib/src/main/scala/aoplib/redundancy/RedundancyAspect.scala new file mode 100644 index 00000000..d2be55c2 --- /dev/null +++ b/aoplib/src/main/scala/aoplib/redundancy/RedundancyAspect.scala @@ -0,0 +1,152 @@ +package aoplib.redundancy + +import aoplib.AnnotationHelpers +import chisel3.Data +import chisel3.core.RawModule +import chisel3.aop.Aspect +import chisel3.experimental.{RunFirrtlTransform} +import firrtl.{AnnotationSeq, CircuitForm, CircuitState, HighForm, LowFirrtlOptimization, LowForm, SourceFlow, MidForm, Namespace, RenameMap, ResolveAndCheck, ResolvedAnnotationPaths, Transform, WRef} +import firrtl.annotations.{Annotation, ReferenceTarget} +import firrtl.stage.RunFirrtlTransformAnnotation + +import scala.collection.mutable +import scala.reflect.runtime.universe.TypeTag + +/** Adds triple redundancy to the selected registers + * + * @param selectRegisters Select registers in a module to give triple redundancy + * @param tTag Needed to prevent type-erasure of the top-level module type + * @tparam T Type of top-level module + */ +case class RedundancyAspect[T <: RawModule](selectRegisters: T => Iterable[Data]) + (implicit tTag: TypeTag[T]) extends Aspect[T] { + override def toAnnotation(top: T): AnnotationSeq = { + Seq(RedundancyRegisters(selectRegisters(top).map(_.toTarget).toList), RunFirrtlTransformAnnotation(new RedundancyTransform)) + } +} + +case class RedundancyRegisters(regs: Seq[ReferenceTarget]) extends Annotation { + override def update(renames: RenameMap): Seq[Annotation] = { + Seq(RedundancyRegisters(AnnotationHelpers.renameMany(regs, renames))) + } +} + +class RedundancyTransform extends Transform with ResolvedAnnotationPaths { + import firrtl.ir._ + import firrtl.Mappers._ + override def inputForm: CircuitForm = MidForm + override def outputForm: CircuitForm = HighForm + + override val annotationClasses: Traversable[Class[_]] = Seq(classOf[RedundancyRegisters]) + + case class RegInfo(red0: String, red1: String, output: String, tpe: Option[Type] = None) + + override def execute(state: CircuitState): CircuitState = { + val redundantRegs = state.annotations.flatMap { + case r: RedundancyRegisters => r.regs + case other => Nil + } + + val regModuleMap = mutable.HashMap[String, Set[String]]() + redundantRegs.foreach { rt => + assert(rt.path == Nil && rt.component == Nil, + s"Cannot have a register reference target with a component or a path: $rt") + regModuleMap(rt.module) = regModuleMap.getOrElse(rt.module, Set.empty[String]) + rt.ref + } + + val newModules = state.circuit.modules.map { + case m: Module if regModuleMap.contains(m.name) => + val regMap = mutable.HashMap[String, RegInfo]() + val ns = Namespace(m) + regModuleMap(m.name).foreach { r => + val red0 = ns.newName(r) + val red1 = ns.newName(r) + regMap(r) = RegInfo(red0, red1, ns.newName(r)) + } + val ret = m map tripleRegs(regMap)// map addMuxing(regMap) + //println(ret.serialize) + ret + case other => other + } + + val newState = state.copy( + circuit = state.circuit.copy(modules = newModules), + annotations = state.annotations.filterNot(_.isInstanceOf[RedundancyRegisters]) + ) + + new ResolveAndCheck().execute(newState) + } + + private def tripleRegs(regMap: mutable.Map[String, RegInfo])(s: Statement): Statement = { + s match { + case d: DefRegister if regMap.contains(d.name) => + regMap(d.name) = regMap(d.name).copy(tpe = Some(d.tpe)) + val info = regMap(d.name) + val wire = DefWire(NoInfo, info.output, info.tpe.get) + + // Hack to change reset value to name of this register + regMap(d.name) = info.copy(output = info.red0) + val reg0 = d.copy(name = info.red0) map changeOutput(regMap) + regMap(d.name) = info.copy(output = info.red1) + val reg1 = d.copy(name = info.red1) map changeOutput(regMap) + //Change back + regMap(d.name) = info.copy(output = info.output) + + val r0 = WRef(d) + val r1 = WRef(info.red0) + val r2 = WRef(info.red1) + val o = WRef(info.output) + val assignment = Seq( + Conditionally(NoInfo, cond(r0, r1, r2), Connect(NoInfo, o, r0), + Conditionally(NoInfo, cond(r1, r2, r0), Connect(NoInfo, o, r1), + Conditionally(NoInfo, cond(r2, r0, r1), Connect(NoInfo, o, r2), Connect(NoInfo, o, r0)) + ) + ) + ) + Block(Seq(d, reg0, reg1, wire) ++ assignment) + case con@Connect(_, w@WRef(reg, _, _, _), expr) if regMap.contains(reg) => + val c = con.copy(expr = changeOutput(regMap)(expr)) + Block(Seq( + c, + c.copy(loc = w.copy(name = regMap(reg).red0)), + c.copy(loc = w.copy(name = regMap(reg).red1)) + )) + case other => other map changeOutput(regMap) map tripleRegs(regMap) + } + } + + private def changeOutput(regMap: collection.Map[String, RegInfo])(expr: Expression): Expression = expr match { + case w@WRef(reg, _, _, SourceFlow) if regMap.contains(reg) => w.copy(name = regMap(reg).output) + case e => e map changeOutput(regMap) + } + + def cond(agree1: WRef, agree2: WRef, disagree: WRef): Expression = { + import firrtl.PrimOps._ + val u1 = UIntType(IntWidth(1)) + val a = DoPrim(Eq, Seq(agree1, agree2), Nil, u1) + val d = DoPrim(Neq, Seq(agree1, disagree), Nil, u1) + DoPrim(And, Seq(a, d), Nil, u1) + } + + private def addMuxing(regMap: collection.Map[String, RegInfo])(s: Statement): Statement = { + val wireDefs = regMap.map { case (reg, info) => + DefWire(NoInfo, info.output, info.tpe.get) + } + val assignments = regMap.map { case (reg, info) => + val r0 = WRef(reg) + val r1 = WRef(info.red0) + val r2 = WRef(info.red1) + val o = WRef(info.output) + val assignment = Block(Seq( + Conditionally(NoInfo, cond(r0, r1, r2), Connect(NoInfo, o, r0), + Conditionally(NoInfo, cond(r1, r2, r0), Connect(NoInfo, o, r1), + Conditionally(NoInfo, cond(r2, r0, r1), Connect(NoInfo, o, r2), Connect(NoInfo, o, r0)) + ) + ) + )) + assignment + } + + Block(Seq(Block(wireDefs.toList), s, Block(assignments.toList))) + } +} diff --git a/aoplib/src/main/scala/aoplib/redundancy/StuckFaultAspect.scala b/aoplib/src/main/scala/aoplib/redundancy/StuckFaultAspect.scala new file mode 100644 index 00000000..03e69ab6 --- /dev/null +++ b/aoplib/src/main/scala/aoplib/redundancy/StuckFaultAspect.scala @@ -0,0 +1,76 @@ +package aoplib.redundancy + +import aoplib.AnnotationHelpers +import chisel3.Data +import chisel3.aop.Aspect +import chisel3.core.{RawModule} +import chisel3.experimental.{RunFirrtlTransform} +import firrtl.{AnnotationSeq, CircuitForm, CircuitState, MidForm, RenameMap, ResolvedAnnotationPaths, Transform, WRef} +import firrtl.annotations.{Annotation, ReferenceTarget} + +import scala.collection.mutable +import scala.reflect.runtime.universe.TypeTag + +case class StuckFaultAspect[DUT <: RawModule, M <: RawModule](selectSignals: DUT => Seq[Data]) + (implicit dutTag: TypeTag[DUT]) extends Aspect[DUT] { + override def toAnnotation(dut: DUT): AnnotationSeq = { + Seq(FaultyRegisters(selectSignals(dut).map(_.toTarget))) + } +} + + +case class FaultyRegisters(regs: Seq[ReferenceTarget]) extends Annotation with RunFirrtlTransform { + override def update(renames: RenameMap): Seq[Annotation] = { + Seq(FaultyRegisters(AnnotationHelpers.renameMany(regs, renames))) + } + override def transformClass: Class[_ <: Transform] = classOf[StuckFaultTransform] + override def toFirrtl = this +} + +class StuckFaultTransform extends Transform with ResolvedAnnotationPaths { + import firrtl.ir._ + import firrtl.Mappers._ + override def inputForm: CircuitForm = MidForm + override def outputForm: CircuitForm = MidForm + + override val annotationClasses: Traversable[Class[_]] = Seq(classOf[FaultyRegisters]) + + override def execute(state: CircuitState): CircuitState = { + val faultyRegs = state.annotations.flatMap { + case r: FaultyRegisters => r.regs + case other => Nil + } + + val regModuleMap = mutable.HashMap[String, Set[String]]() + faultyRegs.foreach { rt => + assert(rt.path == Nil && rt.component == Nil, + s"Cannot have a register reference target with a component or a path: $rt") + regModuleMap(rt.module) = regModuleMap.getOrElse(rt.module, Set.empty[String]) + rt.ref + } + + val newModules = state.circuit.modules.map { + case m: Module if regModuleMap.contains(m.name) => + val ret = m map makeFaulty(regModuleMap(m.name))// map addMuxing(regMap) + ret + case other => other + } + + val newState = state.copy( + circuit = state.circuit.copy(modules = newModules), + annotations = state.annotations.filterNot(_.isInstanceOf[FaultyRegisters]) + ) + + newState + } + + private def makeFaulty(regs: Set[String])(s: Statement): Statement = { + s match { + case con@Connect(_, w@WRef(reg, tpe, _, _), expr) if regs.contains(reg) => + tpe match { + case _: UIntType => con.copy(expr = UIntLiteral(0)) + case _: SIntType => con.copy(expr = SIntLiteral(0)) + } + case other => other map makeFaulty(regs) + } + } +} From 07084e1cc5bfde64d6b73090779ba750239c580c Mon Sep 17 00:00:00 2001 From: John Wright Date: Thu, 31 Oct 2019 14:47:04 -0700 Subject: [PATCH 07/73] Refactor IR/Constraints a bit --- floorplan/src/main/scala/Constraints.scala | 473 +++++++++++++++--- floorplan/src/main/scala/IR.scala | 29 +- floorplan/src/main/scala/Serialization.scala | 27 +- floorplan/src/main/scala/chisel/API.scala | 62 ++- .../main/scala/chisel/ChiselAnnotations.scala | 11 +- .../src/main/scala/compiler/Passes.scala | 26 + floorplan/src/main/scala/firrtl/Passes.scala | 6 +- 7 files changed, 531 insertions(+), 103 deletions(-) diff --git a/floorplan/src/main/scala/Constraints.scala b/floorplan/src/main/scala/Constraints.scala index 911352eb..b7d1a965 100644 --- a/floorplan/src/main/scala/Constraints.scala +++ b/floorplan/src/main/scala/Constraints.scala @@ -1,106 +1,427 @@ // See LICENSE for license details package barstools.floorplan -sealed trait Unit - -// TODO how to cleanly round down when dividing? -final case class Unitless(value: BigInt) extends Unit { - def *(other: Unitless) = Unitless(this.value * other.value) - def /(other: Unitless) = Unitless(this.value / other.value) - def +(other: Unitless) = Unitless(this.value + other.value) - def -(other: Unitless) = Unitless(this.value - other.value) - - def *(other: LengthUnit) = LengthUnit(this.value * other.value) - def /(other: LengthUnit) = LengthUnit(this.value / other.value) - def +(other: LengthUnit) = LengthUnit(this.value + other.value) - def -(other: LengthUnit) = LengthUnit(this.value - other.value) - - def *(other: AreaUnit) = AreaUnit(this.value * other.value) - def /(other: AreaUnit) = AreaUnit(this.value / other.value) - def +(other: AreaUnit) = AreaUnit(this.value + other.value) - def -(other: AreaUnit) = AreaUnit(this.value - other.value) +final case class IllegalUnitArithmeticException(a: HasUnit, b: HasUnit, op: String) extends Exception(s"Cannot ${op} ${a} and ${b}") + +sealed trait HasUnit { + + def *(that: HasUnit): HasUnit + def /(that: HasUnit): HasUnit + def +(that: HasUnit): HasUnit + def -(that: HasUnit): HasUnit + def >(that: HasUnit): Boolean + def <(that: HasUnit): Boolean + def >=(that: HasUnit): Boolean + def <=(that: HasUnit): Boolean + def %(that: HasUnit): HasUnit + def %?(that: HasUnit): Boolean + def gcd(that: HasUnit): HasUnit + def lcm(that: HasUnit): HasUnit + + def isPositive: Boolean + +} + +object Rational { + + def reduced(num: BigInt, denom: BigInt): Rational = { + if (num == BigInt(0)) { + Rational(BigInt(0), BigInt(1)) + } else { + val gcd = num.gcd(denom) + Rational(num/gcd, denom/gcd) + } + } + + def apply(ratio: Double, decimalPrecision: Long = 1000): Rational = { + Rational.reduced(BigInt(scala.math.round(ratio*decimalPrecision)), BigInt(decimalPrecision)) + } + +} + +// TODO how to cleanly round when dividing? +final case class Rational(num: BigInt, denom: BigInt) extends HasUnit { + + assert(denom != 0, "Cannot divide by zero!") + + def invert = Rational(denom, num) + def isZero = num == BigInt(0) + def isPositive = (this.num*this.denom) > BigInt(0) + + private def lcm(a: BigInt, b: BigInt) = (a*b)/a.gcd(b) + + def *(that: HasUnit) = that match { + case that: Rational => Rational.reduced(this.num * that.num, this.denom * that.denom) + case that: LengthUnit => LengthUnit(this.num * that.value / this.denom) + case that: AreaUnit => AreaUnit(this.num * that.value / this.denom) + case _ => throw new IllegalUnitArithmeticException(this, that, "*") + } + + def /(that: HasUnit) = that match { + case that: Rational => Rational.reduced(this.num * that.denom, this.denom * that.num) + case _ => throw new IllegalUnitArithmeticException(this, that, "/") + } + + def +(that: HasUnit) = that match { + case that: Rational => { + val newDenom = lcm(this.denom, that.denom) + val newNum = (this.num * (newDenom / this.denom)) + (that.num * (newDenom / that.denom)) + Rational.reduced(newNum, newDenom) + } + case _ => throw new IllegalUnitArithmeticException(this, that, "+") + } + + def -(that: HasUnit) = that match { + case that: Rational => { + val newDenom = lcm(this.denom, that.denom) + val newNum = (this.num * (newDenom / this.denom)) - (that.num * (newDenom / that.denom)) + Rational.reduced(newNum, newDenom) + } + case _ => throw new IllegalUnitArithmeticException(this, that, "-") + } + + def >(that: HasUnit) = that match { + case that: Rational => { + val newDenom = lcm(this.denom, that.denom) + (this.num * (newDenom / this.denom)) > (that.num * (newDenom / that.denom)) + } + case _ => throw new IllegalUnitArithmeticException(this, that, ">") + } + + def <(that: HasUnit) = that match { + case that: Rational => { + val newDenom = lcm(this.denom, that.denom) + (this.num * (newDenom / this.denom)) < (that.num * (newDenom / that.denom)) + } + case _ => throw new IllegalUnitArithmeticException(this, that, "<") + } + + def >=(that: HasUnit) = that match { + case that: Rational => { + val newDenom = lcm(this.denom, that.denom) + (this.num * (newDenom / this.denom)) >= (that.num * (newDenom / that.denom)) + } + case _ => throw new IllegalUnitArithmeticException(this, that, ">=") + } + + def <=(that: HasUnit) = that match { + case that: Rational => { + val newDenom = lcm(this.denom, that.denom) + (this.num * (newDenom / this.denom)) <= (that.num * (newDenom / that.denom)) + } + case _ => throw new IllegalUnitArithmeticException(this, that, "<=") + } + + def %(that: HasUnit) = that match { + case that: Rational => { + val newDenom = lcm(this.denom, that.denom) + val newNum = (this.num * (newDenom / this.denom)) % (that.num * (newDenom / that.denom)) + Rational.reduced(newNum, newDenom) + } + case _ => throw new IllegalUnitArithmeticException(this, that, "%") + } + + def %?(that: HasUnit) = that match { + case that: Rational => (this % that).asInstanceOf[Rational].isZero + case _ => throw new IllegalUnitArithmeticException(this, that, "%?") + } + + def gcd(that: HasUnit) = that match { + case that: Rational => { + val newDenom = lcm(this.denom, that.denom) + val newNum = (this.num * (newDenom / this.denom)).gcd(that.num * (newDenom / that.denom)) + Rational.reduced(newNum, newDenom) + } + case _ => throw new IllegalUnitArithmeticException(this, that, "gcd") + } + + def lcm(that: HasUnit) = that match { + case that: Rational => { + val newDenom = lcm(this.denom, that.denom) + val newNum = lcm(this.num * (newDenom / this.denom), that.num * (newDenom / that.denom)) + Rational.reduced(newNum, newDenom) + } + case _ => throw new IllegalUnitArithmeticException(this, that, "lcm") + } + +} + +final case class LengthUnit(value: BigInt) extends HasUnit { + + def isPositive = this.value > BigInt(0) + + def *(that: HasUnit) = that match { + case that: Rational => LengthUnit((this.value * that.num) / that.denom) + case that: LengthUnit => AreaUnit(this.value * that.value) + case _ => throw new IllegalUnitArithmeticException(this, that, "*") + } + + def /(that: HasUnit) = that match { + case that: Rational => LengthUnit((this.value * that.denom) / that.num) + case that: LengthUnit => Rational.reduced(this.value, that.value) + case _ => throw new IllegalUnitArithmeticException(this, that, "/") + } + + def +(that: HasUnit) = that match { + case that: LengthUnit => LengthUnit(this.value + that.value) + case _ => throw new IllegalUnitArithmeticException(this, that, "+") + } + + def -(that: HasUnit) = that match { + case that: LengthUnit => LengthUnit(this.value - that.value) + case _ => throw new IllegalUnitArithmeticException(this, that, "-") + } + + def >(that: HasUnit) = that match { + case that: LengthUnit => (this.value > that.value) + case _ => throw new IllegalUnitArithmeticException(this, that, ">") + } + + def <(that: HasUnit) = that match { + case that: LengthUnit => (this.value < that.value) + case _ => throw new IllegalUnitArithmeticException(this, that, "<") + } + + def >=(that: HasUnit) = that match { + case that: LengthUnit => (this.value >= that.value) + case _ => throw new IllegalUnitArithmeticException(this, that, ">=") + } + + def <=(that: HasUnit) = that match { + case that: LengthUnit => (this.value <= that.value) + case _ => throw new IllegalUnitArithmeticException(this, that, "<=") + } + + def %(that: HasUnit) = that match { + case that: LengthUnit => LengthUnit(this.value % that.value) + case _ => throw new IllegalUnitArithmeticException(this, that, "%") + } + + def %?(that: HasUnit) = that match { + case that: LengthUnit => (this % that).asInstanceOf[LengthUnit].value == BigInt(0) + case _ => throw new IllegalUnitArithmeticException(this, that, "%") + } + + def gcd(that: HasUnit) = that match { + case that: LengthUnit => LengthUnit(this.value.gcd(that.value)) + case _ => throw new IllegalUnitArithmeticException(this, that, "gcd") + } + + def lcm(that: HasUnit) = that match { + case that: LengthUnit => LengthUnit(this.value*that.value/this.value.gcd(that.value)) + case _ => throw new IllegalUnitArithmeticException(this, that, "lcm") + } + } -final case class LengthUnit(value: BigInt) extends Unit { - def *(other: LengthUnit) = AreaUnit(this.value * other.value) - def /(other: LengthUnit) = Unitless(this.value / other.value) - def +(other: LengthUnit) = LengthUnit(this.value + other.value) - def -(other: LengthUnit) = LengthUnit(this.value - other.value) +final case class AreaUnit(value: BigInt) extends HasUnit { + + def isPositive = this.value > BigInt(0) + + def *(that: HasUnit) = that match { + case that: Rational => AreaUnit((this.value * that.num) / that.denom) + case _ => throw new IllegalUnitArithmeticException(this, that, "*") + } + + def /(that: HasUnit) = that match { + case that: Rational => AreaUnit((this.value * that.denom) / that.num) + case that: LengthUnit => LengthUnit(this.value / that.value) + case that: AreaUnit => Rational.reduced(this.value, that.value) + case _ => throw new IllegalUnitArithmeticException(this, that, "/") + } + + def +(that: HasUnit) = that match { + case that: AreaUnit => AreaUnit(this.value + that.value) + case _ => throw new IllegalUnitArithmeticException(this, that, "+") + } + + def -(that: HasUnit) = that match { + case that: AreaUnit => AreaUnit(this.value - that.value) + case _ => throw new IllegalUnitArithmeticException(this, that, "-") + } + + def >(that: HasUnit) = that match { + case that: AreaUnit => (this.value > that.value) + case _ => throw new IllegalUnitArithmeticException(this, that, ">") + } + + def <(that: HasUnit) = that match { + case that: AreaUnit => (this.value < that.value) + case _ => throw new IllegalUnitArithmeticException(this, that, "<") + } + + def >=(that: HasUnit) = that match { + case that: AreaUnit => (this.value >= that.value) + case _ => throw new IllegalUnitArithmeticException(this, that, ">=") + } + + def <=(that: HasUnit) = that match { + case that: AreaUnit => (this.value <= that.value) + case _ => throw new IllegalUnitArithmeticException(this, that, "<=") + } + + def %(that: HasUnit) = that match { + case that: AreaUnit => AreaUnit(this.value % that.value) + case _ => throw new IllegalUnitArithmeticException(this, that, "%") + } + + def %?(that: HasUnit) = that match { + case that: AreaUnit => (this % that).asInstanceOf[AreaUnit].value == BigInt(0) + case _ => throw new IllegalUnitArithmeticException(this, that, "%") + } + + def gcd(that: HasUnit) = that match { + case that: AreaUnit => AreaUnit(this.value.gcd(that.value)) + case _ => throw new IllegalUnitArithmeticException(this, that, "gcd") + } + + def lcm(that: HasUnit) = that match { + case that: AreaUnit => AreaUnit(this.value*that.value/this.value.gcd(that.value)) + case _ => throw new IllegalUnitArithmeticException(this, that, "lcm") + } - def *(other: Unitless) = LengthUnit(this.value * other.value) - def /(other: Unitless) = LengthUnit(this.value / other.value) } -final case class AreaUnit(value: BigInt) extends Unit { - def /(other: LengthUnit) = LengthUnit(this.value / other.value) +sealed trait Constraint[T <: HasUnit] { - def *(other: Unitless) = AreaUnit(this.value * other.value) - def /(other: Unitless) = AreaUnit(this.value / other.value) + def and(that: Constraint[T]): Constraint[T] - def /(other: AreaUnit) = Unitless(this.value / other.value) - def +(other: AreaUnit) = AreaUnit(this.value + other.value) - def -(other: AreaUnit) = AreaUnit(this.value - other.value) } -sealed trait Constraint[T <: Unit] +sealed abstract class PrimitiveConstraint[T <: HasUnit] extends Constraint[T] -sealed abstract class Constrained[T <: Unit] extends Constraint[T] +final class Unconstrained[T <: HasUnit] extends PrimitiveConstraint[T] { -sealed abstract class Unconstrained[T <: Unit] extends Constraint[T] -case object UnconstrainedUnitless extends Unconstrained[Unitless] -case object UnconstrainedLength extends Unconstrained[LengthUnit] -case object UnconstrainedArea extends Unconstrained[AreaUnit] + def and(that: Constraint[T]) = that -sealed abstract class ExactConstraint[T <: Unit] extends Constrained[T] { - def value: T } -final case class ExactUnitlessConstraint(value: Unitless) extends ExactConstraint[Unitless] -final case class ExactLengthConstraint(value: LengthUnit) extends ExactConstraint[LengthUnit] -final case class ExactAreaConstraint(value: AreaUnit) extends ExactConstraint[AreaUnit] -sealed abstract class GreaterThanConstraint[T <: Unit] extends Constrained[T] { - def value: T - def inclusive: Boolean +object Unconstrained { + + def apply[T <: HasUnit] = new Unconstrained[T] + } -final case class GreaterThanUnitlessConstraint(value: Unitless, inclusive: Boolean) extends GreaterThanConstraint[Unitless] -final case class GreaterThanLengthConstraint(value: LengthUnit, inclusive: Boolean) extends GreaterThanConstraint[LengthUnit] -final case class GreaterThanAreaConstraint(value: AreaUnit, inclusive: Boolean) extends GreaterThanConstraint[AreaUnit] -sealed abstract class LessThanConstraint[T <: Unit] extends Constrained[T] { - def value: T - def inclusive: Boolean +final case class EqualTo[T <: HasUnit](unit: T) extends PrimitiveConstraint[T] { + + assert(unit.isPositive, "EqualTo value must be postiive") + + def and(that: Constraint[T]) = that match { + case that: EqualTo[T] => if (this.unit == that.unit) this else ImpossibleConstraint(this, that) + case that: GreaterThanOrEqualTo[T] => if (this.unit >= that.unit) this else ImpossibleConstraint(this, that) + case that: LessThanOrEqualTo[T] => if (this.unit <= that.unit) this else ImpossibleConstraint(this, that) + case that: MultipleOf[T] => if (this.unit %? that.unit) this else ImpossibleConstraint(this, that) + case that: ImpossibleConstraint[T] => that + case that: Unconstrained[T] => this + case that => AndConstraint(this, that) + } + } -final case class LessThanUnitlessConstraint(value: Unitless, inclusive: Boolean) extends LessThanConstraint[Unitless] -final case class LessThanLengthConstraint(value: LengthUnit, inclusive: Boolean) extends LessThanConstraint[LengthUnit] -final case class LessThanAreaConstraint(value: AreaUnit, inclusive: Boolean) extends LessThanConstraint[AreaUnit] -sealed abstract class MultipleOfConstraint[T <: Unit] extends Constrained[T] { - def value: T +final case class GreaterThanOrEqualTo[T <: HasUnit](unit: T) extends PrimitiveConstraint[T] { + + assert(unit.isPositive , "GreaterThanOrEqualTo value must be postiive") + + def and(that: Constraint[T]) = that match { + case that: EqualTo[T] => if (this.unit <= that.unit) that else ImpossibleConstraint(this, that) + case that: GreaterThanOrEqualTo[T] => if (this.unit >= that.unit) this else that + case that: LessThanOrEqualTo[T] => if (this.unit < that.unit) AndConstraint(this, that) else + if (this.unit == that.unit) EqualTo(this.unit) else ImpossibleConstraint(this, that) + case that: ImpossibleConstraint[T] => that + case that: Unconstrained[T] => this + case that => AndConstraint(this, that) + } + } -final case class MultipleOfUnitlessConstraint(value: Unitless) extends MultipleOfConstraint[Unitless] -final case class MultipleOfLengthConstraint(value: LengthUnit) extends MultipleOfConstraint[LengthUnit] -final case class MultipleOfAreaConstraint(value: AreaUnit) extends MultipleOfConstraint[AreaUnit] -// TODO do we have discrete combinations (e.g. BetweenConstraint) or do we provide a way to do unions? +final case class LessThanOrEqualTo[T <: HasUnit](unit: T) extends PrimitiveConstraint[T] { + + assert(unit.isPositive, "LessThanOrEqualTo value must be positive") + + def and(that: Constraint[T]) = that match { + case that: EqualTo[T] => if (this.unit >= that.unit) that else ImpossibleConstraint(this, that) + case that: GreaterThanOrEqualTo[T] => if (this.unit > that.unit) AndConstraint(this, that) else + if (this.unit == that.unit) EqualTo(this.unit) else ImpossibleConstraint(this, that) + case that: LessThanOrEqualTo[T] => if (this.unit <= that.unit) this else that + case that: ImpossibleConstraint[T] => that + case that: Unconstrained[T] => this + case that => AndConstraint(this, that) + } -sealed abstract class NotConstraint[T <: Unit] { - def constraint: Constrained[T] } -final case class NotUnitlessConstraint(constraint: Constrained[Unitless]) extends NotConstraint[Unitless] -final case class NotLengthConstraint(constraint: Constrained[LengthUnit]) extends NotConstraint[LengthUnit] -final case class NotAreaConstraint(constraint: Constrained[AreaUnit]) extends NotConstraint[AreaUnit] -sealed abstract class AndConstraint[T <: Unit] { - def constraints: List[Constrained[T]] +// TODO allow offset +final case class MultipleOf[T <: HasUnit](unit: T) extends PrimitiveConstraint[T] { + + assert(unit.isPositive, "MultipleOf value must be positive") + + def and(that: Constraint[T]) = that match { + case that: EqualTo[T] => if (that.unit %? this.unit) that else ImpossibleConstraint(this, that) + case that: MultipleOf[T] => MultipleOf((this.unit lcm that.unit).asInstanceOf[T]) + case that: LessThanOrEqualTo[T] => if (that.unit < this.unit) ImpossibleConstraint(this, that) else AndConstraint(this, that) + case that: ImpossibleConstraint[T] => that + case that: Unconstrained[T] => this + case that => AndConstraint(this, that) + } + } -final case class AndUnitlessConstraint(constraints: List[Constrained[Unitless]]) extends AndConstraint[Unitless] -final case class AndLengthConstraint(constraints: List[Constrained[LengthUnit]]) extends AndConstraint[LengthUnit] -final case class AndAreaConstraint(constraints: List[Constrained[AreaUnit]]) extends AndConstraint[AreaUnit] -sealed abstract class OrConstraint[T <: Unit] { - def constraints: List[Constrained[T]] +sealed abstract class AggregateConstraint[T <: HasUnit] extends Constraint[T] + +final case class ImpossibleConstraint[T <: HasUnit](a: Constraint[T], b: Constraint[T]) extends AggregateConstraint[T] { + + def and(that: Constraint[T]) = this + } -final case class OrUnitlessConstraint(constraints: List[Constrained[Unitless]]) extends OrConstraint[Unitless] -final case class OrLengthConstraint(constraints: List[Constrained[LengthUnit]]) extends OrConstraint[LengthUnit] -final case class OrAreaConstraint(constraints: List[Constrained[AreaUnit]]) extends OrConstraint[AreaUnit] +final case class AndConstraint[T <: HasUnit](constraints: Seq[Constraint[T]]) extends AggregateConstraint[T] { + + def and(that: Constraint[T]) = that match { + case that: ImpossibleConstraint[T] => that + case that: Unconstrained[T] => this + case that => AndConstraint(this, that) + } + + def flatten: AndConstraint[T] = { + AndConstraint(constraints.collect({ + case x: AndConstraint[T] => x.flatten.constraints + case x: PrimitiveConstraint[T] => Seq(x) + }).reduce(_ ++ _)) + } + + def reduce: Constraint[T] = { + val flat = this.flatten.constraints + + val exact = flat.collect({ + case x:EqualTo[T] => x + }).reduceOption[Constraint[T]](_.and(_)) + + val gt = flat.collect({ + case x:GreaterThanOrEqualTo[T] => x + }).reduceOption[Constraint[T]](_.and(_)) + + val lt = flat.collect({ + case x:LessThanOrEqualTo[T] => x + }).reduceOption[Constraint[T]](_.and(_)) + + val mult = flat.collect({ + case x:MultipleOf[T] => x + }).reduceOption[Constraint[T]](_.and(_)) + + // if exact is defined, we'll either be exact or impossible + exact.map({ value => + val postGt = gt.map(_.and(value)).getOrElse(value) + val postLt = lt.map(_.and(postGt)).getOrElse(postGt) + mult.map(_.and(postLt)).getOrElse(postLt) + }).getOrElse { + ??? + } + } + +} + +object AndConstraint { + + def apply[T <: HasUnit](a: Constraint[T], b: Constraint[T]): AndConstraint[T] = AndConstraint(Seq(a, b)) + +} diff --git a/floorplan/src/main/scala/IR.scala b/floorplan/src/main/scala/IR.scala index 97f818d6..6e1119d1 100644 --- a/floorplan/src/main/scala/IR.scala +++ b/floorplan/src/main/scala/IR.scala @@ -1,6 +1,8 @@ // See LICENSE for license details package barstools.floorplan +// TODO Make some of this stuff private + ////////////////////////////////////////////// Base classes sealed abstract class Element { @@ -26,7 +28,7 @@ sealed abstract class ConstrainedRect extends Primitive { val width: Constraint[LengthUnit] val height: Constraint[LengthUnit] val area: Constraint[AreaUnit] - val aspectRatio: Constraint[Unitless] + val aspectRatio: Constraint[Rational] } @@ -38,6 +40,15 @@ sealed abstract class ConcreteRect extends Primitive { } +final case class PlaceholderAbstractRect(name: String) extends AbstractRect + +final case class PlaceholderConstrainedRect( + name: String, + width: Constraint[LengthUnit], + height: Constraint[LengthUnit], + area: Constraint[AreaUnit], + aspectRatio: Constraint[Rational]) extends ConstrainedRect + sealed trait MacroLike final case class AbstractMacro(name: String) extends AbstractRect with MacroLike @@ -51,10 +62,10 @@ sealed trait LogicLike { final case class AbstractLogicRect(name: String, hardBoundary: Boolean) extends AbstractRect with LogicLike { def constrain( - width: Constraint[LengthUnit] = UnconstrainedLength, - height: Constraint[LengthUnit] = UnconstrainedLength, - area: Constraint[AreaUnit] = UnconstrainedArea, - aspectRatio: Constraint[Unitless] = UnconstrainedUnitless) = ConstrainedLogicRect(name, width, height, area, aspectRatio, hardBoundary) + width: Constraint[LengthUnit] = Unconstrained[LengthUnit], + height: Constraint[LengthUnit] = Unconstrained[LengthUnit], + area: Constraint[AreaUnit] = Unconstrained[AreaUnit], + aspectRatio: Constraint[Rational] = Unconstrained[Rational]) = ConstrainedLogicRect(name, width, height, area, aspectRatio, hardBoundary) } @@ -63,7 +74,7 @@ final case class ConstrainedLogicRect( width: Constraint[LengthUnit], height: Constraint[LengthUnit], area: Constraint[AreaUnit], - aspectRatio: Constraint[Unitless], + aspectRatio: Constraint[Rational], hardBoundary: Boolean) extends ConstrainedRect with LogicLike final case class ConcreteLogicRect(name: String, width: LengthUnit, height: LengthUnit, hardBoundary: Boolean) extends ConcreteRect with LogicLike @@ -73,7 +84,7 @@ sealed abstract class Group extends Element sealed abstract class Grid extends Group { val xDim: Int val yDim: Int - val elements: Seq[Element] + val elements: Seq[String] assert(xDim > 0, "X dimension of grid must be positive") assert(yDim > 0, "Y dimension of grid must be positive") @@ -81,11 +92,11 @@ sealed abstract class Grid extends Group { def get(x: Int, y: Int) = elements(xDim*y + x) } -final case class AbstractGrid[T <: Element](name: String, xDim: Int, yDim: Int, elements: Seq[T]) extends Grid { +final case class AbstractGrid(name: String, xDim: Int, yDim: Int, elements: Seq[String]) extends Grid { def level = 2 } -final case class WeightedGrid[T <: Element](name: String, xDim: Int, yDim: Int, elements: Seq[T], weights: Seq[Int], packed: Boolean) extends Grid { +final case class WeightedGrid(name: String, xDim: Int, yDim: Int, elements: Seq[String], weights: Seq[Int], packed: Boolean) extends Grid { def level = 1 } diff --git a/floorplan/src/main/scala/Serialization.scala b/floorplan/src/main/scala/Serialization.scala index 8c78093d..efd8b70d 100644 --- a/floorplan/src/main/scala/Serialization.scala +++ b/floorplan/src/main/scala/Serialization.scala @@ -5,19 +5,26 @@ import org.json4s._ import org.json4s.native.Serialization.{read, write} import scala.reflect.runtime.universe.typeOf -final case class FloorplanElementRecord[T <: Element](path: String, element: T) +final case class FloorplanElementRecord(path: Option[String], element: Element) -final case class FloorplanState(elements: Seq[FloorplanElementRecord[Element]], level: Int) +final case class FloorplanState(elements: Seq[FloorplanElementRecord], level: Int) { -object FloorplanSerialization { + def generateDB: FloorplanState.Database = ??? + +} - private val elements = typeOf[Element].typeSymbol.asClass.knownDirectSubclasses.toList +object FloorplanSerialization { // Because Element is sealed, all of its subclasses are known at compile time, so we can construct type hints for them - val formats = new DefaultFormats { + private val typeHintClasses = Seq( + typeOf[Element], + typeOf[Unit] + ).map(_.typeSymbol.asClass.knownDirectSubclasses.toList).reduce(_ ++ _) + + val formats = (new DefaultFormats { override val typeHintFieldName = "class" - override val typeHints = FullTypeHints(elements map { x => Class.forName(x.fullName) }) - } + override val typeHints = FullTypeHints(typeHintClasses map { x => Class.forName(x.fullName) }) + }) def serialize[T <: Element](elt: T): String = write(elt)(formats) @@ -31,10 +38,12 @@ object FloorplanSerialization { object FloorplanState { - def fromSeq(seq: Seq[FloorplanElementRecord[Element]]): FloorplanState = FloorplanState(seq, seq.map(_.element.level).max) + type Database = Map[String, Element] + + def fromSeq(seq: Seq[FloorplanElementRecord]): FloorplanState = FloorplanState(seq, seq.map(_.element.level).max) def serialize(state: FloorplanState): String = write(state)(FloorplanSerialization.formats) - def serialize(seq: Seq[FloorplanElementRecord[Element]]): String = serialize(fromSeq(seq)) + def serialize(seq: Seq[FloorplanElementRecord]): String = serialize(fromSeq(seq)) def deserialize(str: String): FloorplanState = { implicit val formats = FloorplanSerialization.formats diff --git a/floorplan/src/main/scala/chisel/API.scala b/floorplan/src/main/scala/chisel/API.scala index eb91a8d5..71c26d87 100644 --- a/floorplan/src/main/scala/chisel/API.scala +++ b/floorplan/src/main/scala/chisel/API.scala @@ -1,4 +1,64 @@ // See LICENSE for license details -package barstools.floorplan +package barstools.floorplan.chisel +import chisel3.{RawModule} + +import barstools.floorplan._ + +object Floorplan { + + def createRect[T <: RawModule](m: T, + width: Constraint[LengthUnit] = Unconstrained[LengthUnit], + height: Constraint[LengthUnit] = Unconstrained[LengthUnit], + area: Constraint[AreaUnit] = Unconstrained[AreaUnit], + aspectRatio: Constraint[Rational] = Unconstrained[Rational]) = ??? + +} + +object FloorplanUnits { + + // TODO do we make this an annotation? + private final case class FloorplanUnitsException(message: String) extends Exception(message) + + // This corresponds to the scaling factor between user input and database units, such that the + // smallest database unit will correspond to 1. Currently only supports units <= 1. + // e.g. + // + // db unit | unit + // 0.001 um | 1000 + // 0.005 um | 200 + // 1 um | 1 + // 10 um | error + private var unit = Option.empty[Long] + + def set(x: Double) { + if (x > 1) { + throw new FloorplanUnitsException("Cannot set base unit to a value > 1.") + } + if (unit.nonEmpty) { + throw new FloorplanUnitsException("Cannot set units more than once!") + } + unit = Some(scala.math.round(1/x)) + } + + def get = unit + + def area(x: Double): AreaUnit = { + unit.map { u => + AreaUnit(scala.math.round(u*x*x)) + }.getOrElse { + throw new FloorplanUnitsException("Cannot create floorplan with concrete units- units have not been set! Call FloorplanUnits.set first.") + } + } + + def length(x: Double): LengthUnit = { + unit.map { u => + LengthUnit(scala.math.round(u*x)) + }.getOrElse { + throw new FloorplanUnitsException("Cannot create floorplan with concrete units- units have not been set! Call FloorplanUnits.set first.") + } + } + + +} diff --git a/floorplan/src/main/scala/chisel/ChiselAnnotations.scala b/floorplan/src/main/scala/chisel/ChiselAnnotations.scala index 2496a841..d2fc2256 100644 --- a/floorplan/src/main/scala/chisel/ChiselAnnotations.scala +++ b/floorplan/src/main/scala/chisel/ChiselAnnotations.scala @@ -2,18 +2,19 @@ package barstools.floorplan.chisel import barstools.floorplan.firrtl.{FloorplanModuleAnnotation, GenerateFloorplanIRPass} -import barstools.floorplan.{Element} -import chisel3.core.{RawModule} +import barstools.floorplan.{Element, Group, Primitive} +import chisel3.{RawModule} import chisel3.experimental.{annotate, ChiselAnnotation} + import firrtl.stage.RunFirrtlTransformAnnotation -object Floorplan { +private object FloorplanAnnotation { private var annotatedPass = false - def setLayout[T <: RawModule, U <: Element](m: T, fpElement: U): Unit = { - annotate(new ChiselAnnotation { def toFirrtl: FloorplanModuleAnnotation = FloorplanModuleAnnotation(m.toNamed.toTarget, fpElement.serialize) }) + def apply[T <: RawModule, U <: Primitive](m: T, fpElement: U): Unit = { + annotate(new ChiselAnnotation { def toFirrtl: FloorplanModuleAnnotation = FloorplanModuleAnnotation(m.toTarget, fpElement.serialize) }) // Only add the RunFirrtlTransformAnnotation once if (!annotatedPass) { diff --git a/floorplan/src/main/scala/compiler/Passes.scala b/floorplan/src/main/scala/compiler/Passes.scala index c4b1330b..51f00ad7 100644 --- a/floorplan/src/main/scala/compiler/Passes.scala +++ b/floorplan/src/main/scala/compiler/Passes.scala @@ -9,3 +9,29 @@ abstract class Pass { } +class SidebandAnnotationPass { + + def execute(state: FloorplanState): FloorplanState = ??? + +} + +class MacroLayoutsPass { + + def execute(state: FloorplanState): FloorplanState = ??? + +} + + +class BottomUpPropagationPass { + + def execute(state: FloorplanState): FloorplanState = ??? + +} + +class TopDownPropagationPass { + + def execute(state: FloorplanState): FloorplanState = ??? + +} + + diff --git a/floorplan/src/main/scala/firrtl/Passes.scala b/floorplan/src/main/scala/firrtl/Passes.scala index a27efd45..b625a829 100644 --- a/floorplan/src/main/scala/firrtl/Passes.scala +++ b/floorplan/src/main/scala/firrtl/Passes.scala @@ -34,8 +34,8 @@ class GenerateFloorplanIRPass extends Transform with RegisteredTransform { graph.findInstancesInHierarchy(x.target.name). map(_.tail.map(_.name)). reduce(_ ++ _). - map(FloorplanElementRecord(_, FloorplanSerialization.deserialize(x.fpir))) - }).reduce(_ ++ _)).foreach { list => + map(y => FloorplanElementRecord(Some(y), FloorplanSerialization.deserialize(x.fpir))) + }).reduceOption(_ ++ _)).foreach(_.foreach { list => val filename = state.annotations.collectFirst({ case x: FloorplanIRFileAnnotation => x.value }).getOrElse { @@ -45,7 +45,7 @@ class GenerateFloorplanIRPass extends Transform with RegisteredTransform { val writer = new java.io.FileWriter(filename) writer.write(FloorplanState.serialize(list)) writer.close() - } + }) state } } From e04bc6281ad3914b8585e04952a49f309b2c447d Mon Sep 17 00:00:00 2001 From: Colin Schmidt Date: Thu, 31 Oct 2019 18:07:48 -0700 Subject: [PATCH 08/73] Fix? generate floorplan IR pass --- floorplan/src/main/scala/firrtl/Passes.scala | 25 +++++++++++--------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/floorplan/src/main/scala/firrtl/Passes.scala b/floorplan/src/main/scala/firrtl/Passes.scala index b625a829..2bbdfba6 100644 --- a/floorplan/src/main/scala/firrtl/Passes.scala +++ b/floorplan/src/main/scala/firrtl/Passes.scala @@ -29,23 +29,26 @@ class GenerateFloorplanIRPass extends Transform with RegisteredTransform { def execute(state: CircuitState): CircuitState = { // TODO don't need graph if there are no annos, which can be a speedup val graph = new InstanceGraph(state.circuit) - optSeq(state.annotations.collect({ + state.annotations.collect({ case x: FloorplanModuleAnnotation => graph.findInstancesInHierarchy(x.target.name). map(_.tail.map(_.name)). reduce(_ ++ _). map(y => FloorplanElementRecord(Some(y), FloorplanSerialization.deserialize(x.fpir))) - }).reduceOption(_ ++ _)).foreach(_.foreach { list => - val filename = state.annotations.collectFirst({ - case x: FloorplanIRFileAnnotation => x.value - }).getOrElse { - val opt = options.head.longOption - throw new Exception(s"Did not specify a filename for GenerateFloorplanIRPass. Please provide a FloorplanIRFileAnnotation or use the --${opt} option.") + }).reduceOption(_ ++ _).foreach{ + _.foreach { + list => + val filename = state.annotations.collectFirst({ + case x: FloorplanIRFileAnnotation => x.value + }).getOrElse { + val opt = options.head.longOption + throw new Exception(s"Did not specify a filename for GenerateFloorplanIRPass. Please provide a FloorplanIRFileAnnotation or use the --${opt} option.") + } + val writer = new java.io.FileWriter(filename) + writer.write(FloorplanState.serialize(Seq(list))) + writer.close() } - val writer = new java.io.FileWriter(filename) - writer.write(FloorplanState.serialize(list)) - writer.close() - }) + } state } } From 2ae5046fd966700e8e919d54ef0005d9f44f1a4e Mon Sep 17 00:00:00 2001 From: John Wright Date: Thu, 31 Oct 2019 19:14:14 -0700 Subject: [PATCH 09/73] Clean up firrtl pass --- floorplan/src/main/scala/firrtl/Passes.scala | 27 +++++++------------- 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/floorplan/src/main/scala/firrtl/Passes.scala b/floorplan/src/main/scala/firrtl/Passes.scala index 2bbdfba6..036d1ede 100644 --- a/floorplan/src/main/scala/firrtl/Passes.scala +++ b/floorplan/src/main/scala/firrtl/Passes.scala @@ -20,12 +20,6 @@ class GenerateFloorplanIRPass extends Transform with RegisteredTransform { ) ) - private def optSeq[T](s: Seq[T]): Option[Seq[T]] = { - // This works in scala 2.13 - //Option.when(s.nonEmpty)(s) - if (s.nonEmpty) Some(s) else None - } - def execute(state: CircuitState): CircuitState = { // TODO don't need graph if there are no annos, which can be a speedup val graph = new InstanceGraph(state.circuit) @@ -35,19 +29,16 @@ class GenerateFloorplanIRPass extends Transform with RegisteredTransform { map(_.tail.map(_.name)). reduce(_ ++ _). map(y => FloorplanElementRecord(Some(y), FloorplanSerialization.deserialize(x.fpir))) - }).reduceOption(_ ++ _).foreach{ - _.foreach { - list => - val filename = state.annotations.collectFirst({ - case x: FloorplanIRFileAnnotation => x.value - }).getOrElse { - val opt = options.head.longOption - throw new Exception(s"Did not specify a filename for GenerateFloorplanIRPass. Please provide a FloorplanIRFileAnnotation or use the --${opt} option.") - } - val writer = new java.io.FileWriter(filename) - writer.write(FloorplanState.serialize(Seq(list))) - writer.close() + }).flatten.foreach { list => + val filename = state.annotations.collectFirst({ + case x: FloorplanIRFileAnnotation => x.value + }).getOrElse { + val opt = options.head.longOption + throw new Exception(s"Did not specify a filename for GenerateFloorplanIRPass. Please provide a FloorplanIRFileAnnotation or use the --${opt} option.") } + val writer = new java.io.FileWriter(filename) + writer.write(FloorplanState.serialize(Seq(list))) + writer.close() } state } From 65dac5afef329db804ded22ee4a565b8c4c5cfdc Mon Sep 17 00:00:00 2001 From: John Wright Date: Fri, 1 Nov 2019 11:01:18 -0700 Subject: [PATCH 10/73] Make grids work in the Chisel API --- floorplan/src/main/scala/Constraints.scala | 8 +- floorplan/src/main/scala/IR.scala | 44 +++---- floorplan/src/main/scala/chisel/API.scala | 113 +++++++++++++++++- .../main/scala/chisel/ChiselAnnotations.scala | 4 +- 4 files changed, 137 insertions(+), 32 deletions(-) diff --git a/floorplan/src/main/scala/Constraints.scala b/floorplan/src/main/scala/Constraints.scala index b7d1a965..d18fa117 100644 --- a/floorplan/src/main/scala/Constraints.scala +++ b/floorplan/src/main/scala/Constraints.scala @@ -1,7 +1,7 @@ // See LICENSE for license details package barstools.floorplan -final case class IllegalUnitArithmeticException(a: HasUnit, b: HasUnit, op: String) extends Exception(s"Cannot ${op} ${a} and ${b}") +private[floorplan] final case class IllegalUnitArithmeticException(a: HasUnit, b: HasUnit, op: String) extends Exception(s"Cannot ${op} ${a} and ${b}") sealed trait HasUnit { @@ -37,6 +37,8 @@ object Rational { Rational.reduced(BigInt(scala.math.round(ratio*decimalPrecision)), BigInt(decimalPrecision)) } + def apply(l: Long): Rational = Rational(l, 1) + } // TODO how to cleanly round when dividing? @@ -146,7 +148,7 @@ final case class Rational(num: BigInt, denom: BigInt) extends HasUnit { } -final case class LengthUnit(value: BigInt) extends HasUnit { +final case class LengthUnit private[floorplan] (value: BigInt) extends HasUnit { def isPositive = this.value > BigInt(0) @@ -214,7 +216,7 @@ final case class LengthUnit(value: BigInt) extends HasUnit { } -final case class AreaUnit(value: BigInt) extends HasUnit { +final case class AreaUnit private[floorplan] (value: BigInt) extends HasUnit { def isPositive = this.value > BigInt(0) diff --git a/floorplan/src/main/scala/IR.scala b/floorplan/src/main/scala/IR.scala index 6e1119d1..72feb2be 100644 --- a/floorplan/src/main/scala/IR.scala +++ b/floorplan/src/main/scala/IR.scala @@ -6,6 +6,7 @@ package barstools.floorplan ////////////////////////////////////////////// Base classes sealed abstract class Element { + val name: String // TODO make this an Enumeration @@ -40,36 +41,24 @@ sealed abstract class ConcreteRect extends Primitive { } -final case class PlaceholderAbstractRect(name: String) extends AbstractRect - -final case class PlaceholderConstrainedRect( +private[floorplan] final case class ConstrainedPlaceholderRect( name: String, - width: Constraint[LengthUnit], - height: Constraint[LengthUnit], - area: Constraint[AreaUnit], - aspectRatio: Constraint[Rational]) extends ConstrainedRect + width: Constraint[LengthUnit] = Unconstrained[LengthUnit], + height: Constraint[LengthUnit] = Unconstrained[LengthUnit], + area: Constraint[AreaUnit] = Unconstrained[AreaUnit], + aspectRatio: Constraint[Rational] = Unconstrained[Rational]) extends ConstrainedRect sealed trait MacroLike -final case class AbstractMacro(name: String) extends AbstractRect with MacroLike +private[floorplan] final case class AbstractMacro(name: String) extends AbstractRect with MacroLike -final case class ConcreteMacro(name: String, width: LengthUnit, height: LengthUnit) extends ConcreteRect with MacroLike +private[floorplan] final case class ConcreteMacro(name: String, width: LengthUnit, height: LengthUnit) extends ConcreteRect with MacroLike sealed trait LogicLike { val hardBoundary: Boolean } -final case class AbstractLogicRect(name: String, hardBoundary: Boolean) extends AbstractRect with LogicLike { - - def constrain( - width: Constraint[LengthUnit] = Unconstrained[LengthUnit], - height: Constraint[LengthUnit] = Unconstrained[LengthUnit], - area: Constraint[AreaUnit] = Unconstrained[AreaUnit], - aspectRatio: Constraint[Rational] = Unconstrained[Rational]) = ConstrainedLogicRect(name, width, height, area, aspectRatio, hardBoundary) - -} - -final case class ConstrainedLogicRect( +private[floorplan] final case class ConstrainedLogicRect( name: String, width: Constraint[LengthUnit], height: Constraint[LengthUnit], @@ -77,7 +66,7 @@ final case class ConstrainedLogicRect( aspectRatio: Constraint[Rational], hardBoundary: Boolean) extends ConstrainedRect with LogicLike -final case class ConcreteLogicRect(name: String, width: LengthUnit, height: LengthUnit, hardBoundary: Boolean) extends ConcreteRect with LogicLike +private[floorplan] final case class ConcreteLogicRect(name: String, width: LengthUnit, height: LengthUnit, hardBoundary: Boolean) extends ConcreteRect with LogicLike sealed abstract class Group extends Element @@ -92,11 +81,14 @@ sealed abstract class Grid extends Group { def get(x: Int, y: Int) = elements(xDim*y + x) } -final case class AbstractGrid(name: String, xDim: Int, yDim: Int, elements: Seq[String]) extends Grid { - def level = 2 -} - -final case class WeightedGrid(name: String, xDim: Int, yDim: Int, elements: Seq[String], weights: Seq[Int], packed: Boolean) extends Grid { +private[floorplan] final case class WeightedGrid( + name: String, + xDim: Int, + yDim: Int, + elements: Seq[String], + weights: Seq[Rational], + packed: Boolean +) extends Grid { def level = 1 } diff --git a/floorplan/src/main/scala/chisel/API.scala b/floorplan/src/main/scala/chisel/API.scala index 71c26d87..129f4aa4 100644 --- a/floorplan/src/main/scala/chisel/API.scala +++ b/floorplan/src/main/scala/chisel/API.scala @@ -4,6 +4,9 @@ package barstools.floorplan.chisel import chisel3.{RawModule} import barstools.floorplan._ +import scala.collection.mutable.{ArraySeq, HashMap, Set, HashSet} + +final case class ChiselFloorplanException(message: String) extends Exception(message: String) object Floorplan { @@ -11,7 +14,39 @@ object Floorplan { width: Constraint[LengthUnit] = Unconstrained[LengthUnit], height: Constraint[LengthUnit] = Unconstrained[LengthUnit], area: Constraint[AreaUnit] = Unconstrained[AreaUnit], - aspectRatio: Constraint[Rational] = Unconstrained[Rational]) = ??? + aspectRatio: Constraint[Rational] = Unconstrained[Rational], + hardBoundary: Boolean = true + ) = new ChiselLogicRect(m, width, height, area, aspectRatio, hardBoundary) + + def createGrid[T <: RawModule](m: T, + name: String, + x: Int = 1, + y: Int = 1, + packed: Boolean = false + ) = new ChiselWeightedGrid(name, m, x, y, packed) + +} + +private[chisel] object FloorplanDatabase { + + private val map = new HashMap[RawModule, Set[String]]() + + private def getSet(module: RawModule) = map.getOrElseUpdate(module, new HashSet[String]) + + def register(module: RawModule, name: String): Unit = { + val set = getSet(module) + if (set.contains(name)) { + throw new ChiselFloorplanException(s"Duplicate floorplan element registration ${name} for module ${module.getClass.getName}!") + } + set.add(name) + } + + def getUnusedName(module: RawModule, suggestion: String = "unnamed"): String = { + val set = getSet(module) + var id = 0 + while (set.contains(suggestion + s"_${id}")) { id = id + 1 } + suggestion + s"_${id}" + } } @@ -59,6 +94,82 @@ object FloorplanUnits { } } +} + +abstract class ChiselElement { + val module: RawModule + val name: String + final private def getName = name + + final private var committed = false + + protected def generateElement(): Element + + final private[chisel] def commit(): Element = { + if (committed) throw new ChiselFloorplanException("Cannot commit floorplan more than once!") + committed = true + val fpir = generateElement() + FloorplanAnnotation(module, fpir) + fpir + } + + FloorplanDatabase.register(module, getName) +} + +final class ChiselLogicRect private[chisel] ( + val module: RawModule, + width: Constraint[LengthUnit], + height: Constraint[LengthUnit], + area: Constraint[AreaUnit], + aspectRatio: Constraint[Rational], + hardBoundary: Boolean +) extends ChiselElement { + + val name = "top" // TODO better name for this + + protected def generateElement(): Element = ConstrainedLogicRect(name, width, height, area, aspectRatio, hardBoundary) + +} + +final class ChiselPlaceholderRect private[chisel] ( + val module: RawModule, + width: Constraint[LengthUnit] = Unconstrained[LengthUnit], + height: Constraint[LengthUnit] = Unconstrained[LengthUnit], + area: Constraint[AreaUnit] = Unconstrained[AreaUnit], + aspectRatio: Constraint[Rational] = Unconstrained[Rational] +) extends ChiselElement { + + val name = FloorplanDatabase.getUnusedName(module) + + protected def generateElement(): Element = ConstrainedPlaceholderRect(name, width, height, area, aspectRatio) } +final class ChiselWeightedGrid private[chisel] (val name: String, val module: RawModule, x: Int, y: Int, packed: Boolean) extends ChiselElement { + + assert(x > 0) + assert(y > 0) + + // TODO add resizing APIs + private var xDim = x + private var yDim = y + + private val elements = ArraySeq.fill[Option[ChiselElement]](xDim, yDim) { Option.empty[ChiselElement] } + private val weights = ArraySeq.fill[Rational](xDim, yDim) { Rational(1) } + + def set(x: Int, y: Int, element: ChiselElement, weight: Rational = Rational(1)): Unit = { + if (x >= xDim) throw new IndexOutOfBoundsException(s"X-value ${x} >= ${xDim} in ChiselWeightedGrid") + if (y >= yDim) throw new IndexOutOfBoundsException(s"Y-value ${y} >= ${yDim} in ChiselWeightedGrid") + elements(x)(y) = Some(element) + weights(x)(y) = weight + } + + protected def generateElement(): Element = WeightedGrid( + name, + xDim, + yDim, + elements.flatten.map(_.getOrElse(new ChiselPlaceholderRect(module)).commit().name), + weights.flatten, + packed) + +} diff --git a/floorplan/src/main/scala/chisel/ChiselAnnotations.scala b/floorplan/src/main/scala/chisel/ChiselAnnotations.scala index d2fc2256..cd19459f 100644 --- a/floorplan/src/main/scala/chisel/ChiselAnnotations.scala +++ b/floorplan/src/main/scala/chisel/ChiselAnnotations.scala @@ -2,7 +2,7 @@ package barstools.floorplan.chisel import barstools.floorplan.firrtl.{FloorplanModuleAnnotation, GenerateFloorplanIRPass} -import barstools.floorplan.{Element, Group, Primitive} +import barstools.floorplan.{Element} import chisel3.{RawModule} import chisel3.experimental.{annotate, ChiselAnnotation} @@ -13,7 +13,7 @@ private object FloorplanAnnotation { private var annotatedPass = false - def apply[T <: RawModule, U <: Primitive](m: T, fpElement: U): Unit = { + def apply[T <: RawModule, U <: Element](m: T, fpElement: U): Unit = { annotate(new ChiselAnnotation { def toFirrtl: FloorplanModuleAnnotation = FloorplanModuleAnnotation(m.toTarget, fpElement.serialize) }) // Only add the RunFirrtlTransformAnnotation once From e756cf3b54d154f0e0ce0a12beffde4f0a2b2813 Mon Sep 17 00:00:00 2001 From: John Wright Date: Fri, 1 Nov 2019 15:33:27 -0700 Subject: [PATCH 11/73] Aspect compatibility --- .../aoplib/floorplan/FloorplanAspect.scala | 32 ++----------------- floorplan/src/main/scala/Serialization.scala | 9 ++++-- floorplan/src/main/scala/chisel/API.scala | 27 ++++++++++++---- .../main/scala/chisel/ChiselAnnotations.scala | 29 ----------------- .../src/main/scala/firrtl/Annotations.scala | 6 ++++ 5 files changed, 35 insertions(+), 68 deletions(-) delete mode 100644 floorplan/src/main/scala/chisel/ChiselAnnotations.scala diff --git a/aoplib/src/main/scala/aoplib/floorplan/FloorplanAspect.scala b/aoplib/src/main/scala/aoplib/floorplan/FloorplanAspect.scala index 33f42081..5af7c684 100644 --- a/aoplib/src/main/scala/aoplib/floorplan/FloorplanAspect.scala +++ b/aoplib/src/main/scala/aoplib/floorplan/FloorplanAspect.scala @@ -22,36 +22,8 @@ case class MemberTracker(name: String, targets: Seq[IsMember], finalSelection: S } } -/* -case class FloorplanAspect[T <: RawModule](name: String, dir: String, buildFloorplan: T => LayoutBase) - (implicit tTag: TypeTag[T]) extends Aspect[T] { - def collectTrackers(layout: LayoutBase): Seq[MemberTracker] = { - def visit(layout: LayoutBase): Seq[MemberTracker] = { - val trackers = if(layout.properties.get(TargetKey).nonEmpty) { - val (target, finalMapping) = layout.get(TargetKey).asInstanceOf[(IsMember, Seq[IsMember] => Option[IsMember])] - Seq(MemberTracker(layout.name, Seq(target), finalMapping)) - } else Nil - layout match { - case arr: ArrayLayout => trackers ++ arr.elements.flatMap(visit) - case other => trackers - } - } - visit(layout) - } - override def toAnnotation(top: T): AnnotationSeq = { - val layout = buildFloorplan(top) - val trackers = collectTrackers(layout) - Seq(FloorplanInfo(layout, dir, name), RunFirrtlTransformAnnotation(new FloorplanTransform())) ++ trackers - } -} -*/ - -case class FloorplanAspectNew[T <: RawModule](name: String, dir: String, buildFloorplan: T => AnnotationSeq) - (implicit tTag: TypeTag[T]) extends Aspect[T] { +case class FloorplanAspect[T <: RawModule](buildFloorplan: T => AnnotationSeq)(implicit tTag: TypeTag[T]) extends Aspect[T] { override def toAnnotation(top: T): AnnotationSeq = { - buildFloorplan(top) ++ Seq( - RunFirrtlTransformAnnotation(new barstools.floorplan.firrtl.GenerateFloorplanIRPass()), - barstools.floorplan.firrtl.FloorplanIRFileAnnotation("floorplan.ir") - ) + buildFloorplan(top) :+ RunFirrtlTransformAnnotation(new barstools.floorplan.firrtl.GenerateFloorplanIRPass()) } } diff --git a/floorplan/src/main/scala/Serialization.scala b/floorplan/src/main/scala/Serialization.scala index efd8b70d..c0bc53f0 100644 --- a/floorplan/src/main/scala/Serialization.scala +++ b/floorplan/src/main/scala/Serialization.scala @@ -2,7 +2,7 @@ package barstools.floorplan import org.json4s._ -import org.json4s.native.Serialization.{read, write} +import org.json4s.native.Serialization.{read, write, writePretty} import scala.reflect.runtime.universe.typeOf final case class FloorplanElementRecord(path: Option[String], element: Element) @@ -18,7 +18,10 @@ object FloorplanSerialization { // Because Element is sealed, all of its subclasses are known at compile time, so we can construct type hints for them private val typeHintClasses = Seq( typeOf[Element], - typeOf[Unit] + typeOf[Unit], + typeOf[Constraint[Rational]], + typeOf[Constraint[LengthUnit]], + typeOf[Constraint[AreaUnit]], ).map(_.typeSymbol.asClass.knownDirectSubclasses.toList).reduce(_ ++ _) val formats = (new DefaultFormats { @@ -42,7 +45,7 @@ object FloorplanState { def fromSeq(seq: Seq[FloorplanElementRecord]): FloorplanState = FloorplanState(seq, seq.map(_.element.level).max) - def serialize(state: FloorplanState): String = write(state)(FloorplanSerialization.formats) + def serialize(state: FloorplanState): String = writePretty(state)(FloorplanSerialization.formats) def serialize(seq: Seq[FloorplanElementRecord]): String = serialize(fromSeq(seq)) def deserialize(str: String): FloorplanState = { diff --git a/floorplan/src/main/scala/chisel/API.scala b/floorplan/src/main/scala/chisel/API.scala index 129f4aa4..72888c97 100644 --- a/floorplan/src/main/scala/chisel/API.scala +++ b/floorplan/src/main/scala/chisel/API.scala @@ -4,6 +4,7 @@ package barstools.floorplan.chisel import chisel3.{RawModule} import barstools.floorplan._ +import barstools.floorplan.firrtl.FloorplanModuleAnnotation import scala.collection.mutable.{ArraySeq, HashMap, Set, HashSet} final case class ChiselFloorplanException(message: String) extends Exception(message: String) @@ -25,19 +26,24 @@ object Floorplan { packed: Boolean = false ) = new ChiselWeightedGrid(name, m, x, y, packed) + def commitAndGetAnnotations(): Seq[FloorplanModuleAnnotation] = FloorplanDatabase.commitAndGetAnnotations() + } private[chisel] object FloorplanDatabase { - private val map = new HashMap[RawModule, Set[String]]() + private val nameMap = new HashMap[RawModule, Set[String]]() + private val elements = new HashSet[ChiselElement]() - private def getSet(module: RawModule) = map.getOrElseUpdate(module, new HashSet[String]) + private def getSet(module: RawModule) = nameMap.getOrElseUpdate(module, new HashSet[String]) - def register(module: RawModule, name: String): Unit = { + def register(module: RawModule, element: ChiselElement): Unit = { + val name = element.name val set = getSet(module) if (set.contains(name)) { throw new ChiselFloorplanException(s"Duplicate floorplan element registration ${name} for module ${module.getClass.getName}!") } + elements.add(element) set.add(name) } @@ -48,6 +54,8 @@ private[chisel] object FloorplanDatabase { suggestion + s"_${id}" } + def commitAndGetAnnotations(): Seq[FloorplanModuleAnnotation] = elements.toSeq.map(_.getAnnotation()) + } object FloorplanUnits { @@ -105,15 +113,22 @@ abstract class ChiselElement { protected def generateElement(): Element + // TODO FIXME this is clunky + final private var fpir: Element = null + + def getAnnotation(): FloorplanModuleAnnotation = { + if (!committed) this.commit() + FloorplanModuleAnnotation(module.toTarget, fpir) + } + final private[chisel] def commit(): Element = { if (committed) throw new ChiselFloorplanException("Cannot commit floorplan more than once!") committed = true - val fpir = generateElement() - FloorplanAnnotation(module, fpir) + fpir = generateElement() fpir } - FloorplanDatabase.register(module, getName) + FloorplanDatabase.register(module, this) } final class ChiselLogicRect private[chisel] ( diff --git a/floorplan/src/main/scala/chisel/ChiselAnnotations.scala b/floorplan/src/main/scala/chisel/ChiselAnnotations.scala deleted file mode 100644 index cd19459f..00000000 --- a/floorplan/src/main/scala/chisel/ChiselAnnotations.scala +++ /dev/null @@ -1,29 +0,0 @@ -// See LICENSE for license details -package barstools.floorplan.chisel - -import barstools.floorplan.firrtl.{FloorplanModuleAnnotation, GenerateFloorplanIRPass} -import barstools.floorplan.{Element} -import chisel3.{RawModule} -import chisel3.experimental.{annotate, ChiselAnnotation} - -import firrtl.stage.RunFirrtlTransformAnnotation - - -private object FloorplanAnnotation { - - private var annotatedPass = false - - def apply[T <: RawModule, U <: Element](m: T, fpElement: U): Unit = { - annotate(new ChiselAnnotation { def toFirrtl: FloorplanModuleAnnotation = FloorplanModuleAnnotation(m.toTarget, fpElement.serialize) }) - - // Only add the RunFirrtlTransformAnnotation once - if (!annotatedPass) { - annotate(new ChiselAnnotation { - def toFirrtl: RunFirrtlTransformAnnotation = new RunFirrtlTransformAnnotation(new GenerateFloorplanIRPass()) - }) - annotatedPass = true - } - } - -} - diff --git a/floorplan/src/main/scala/firrtl/Annotations.scala b/floorplan/src/main/scala/firrtl/Annotations.scala index 18982d66..6533e76a 100644 --- a/floorplan/src/main/scala/firrtl/Annotations.scala +++ b/floorplan/src/main/scala/firrtl/Annotations.scala @@ -16,4 +16,10 @@ case class FloorplanModuleAnnotation(target: ModuleTarget, fpir: String) extends def duplicate(t: ModuleTarget) = this.copy(target, fpir) } +object FloorplanModuleAnnotation { + + def apply(target: ModuleTarget, element: Element): FloorplanModuleAnnotation = FloorplanModuleAnnotation(target, element.serialize) + +} + case class FloorplanIRFileAnnotation(value: String) extends NoTargetAnnotation From c213ea98041e342119f36809c7c9ade2b591e041 Mon Sep 17 00:00:00 2001 From: John Wright Date: Fri, 1 Nov 2019 22:54:48 -0700 Subject: [PATCH 12/73] Fix naming and serialization; still need to figure out how to serialize grid names correctly --- floorplan/src/main/scala/Serialization.scala | 2 +- floorplan/src/main/scala/chisel/API.scala | 53 ++++++++++++++------ floorplan/src/main/scala/firrtl/Passes.scala | 13 ++--- 3 files changed, 47 insertions(+), 21 deletions(-) diff --git a/floorplan/src/main/scala/Serialization.scala b/floorplan/src/main/scala/Serialization.scala index c0bc53f0..ee1ecf79 100644 --- a/floorplan/src/main/scala/Serialization.scala +++ b/floorplan/src/main/scala/Serialization.scala @@ -5,7 +5,7 @@ import org.json4s._ import org.json4s.native.Serialization.{read, write, writePretty} import scala.reflect.runtime.universe.typeOf -final case class FloorplanElementRecord(path: Option[String], element: Element) +final case class FloorplanElementRecord(path: String, element: Element) final case class FloorplanState(elements: Seq[FloorplanElementRecord], level: Int) { diff --git a/floorplan/src/main/scala/chisel/API.scala b/floorplan/src/main/scala/chisel/API.scala index 72888c97..b6bd8e31 100644 --- a/floorplan/src/main/scala/chisel/API.scala +++ b/floorplan/src/main/scala/chisel/API.scala @@ -11,20 +11,41 @@ final case class ChiselFloorplanException(message: String) extends Exception(mes object Floorplan { - def createRect[T <: RawModule](m: T, + def createRect[T <: RawModule](module: T, width: Constraint[LengthUnit] = Unconstrained[LengthUnit], height: Constraint[LengthUnit] = Unconstrained[LengthUnit], area: Constraint[AreaUnit] = Unconstrained[AreaUnit], aspectRatio: Constraint[Rational] = Unconstrained[Rational], hardBoundary: Boolean = true - ) = new ChiselLogicRect(m, width, height, area, aspectRatio, hardBoundary) + ) = { + val elt = new ChiselLogicRect(module, width, height, area, aspectRatio, hardBoundary) + FloorplanDatabase.register(module, elt) + elt + } + + def createPlaceholderRect[T <: RawModule](module: T, + name: Option[String] = None, + width: Constraint[LengthUnit] = Unconstrained[LengthUnit], + height: Constraint[LengthUnit] = Unconstrained[LengthUnit], + area: Constraint[AreaUnit] = Unconstrained[AreaUnit], + aspectRatio: Constraint[Rational] = Unconstrained[Rational] + ) = { + val nameStr = name.getOrElse(FloorplanDatabase.getUnusedName(module)) + val elt = new ChiselPlaceholderRect(module, nameStr, width, height, area, aspectRatio) + FloorplanDatabase.register(module, elt) + elt + } - def createGrid[T <: RawModule](m: T, + def createGrid[T <: RawModule](module: T, name: String, x: Int = 1, y: Int = 1, packed: Boolean = false - ) = new ChiselWeightedGrid(name, m, x, y, packed) + ) = { + val elt = new ChiselWeightedGrid(name, module, x, y, packed) + FloorplanDatabase.register(module, elt) + elt + } def commitAndGetAnnotations(): Seq[FloorplanModuleAnnotation] = FloorplanDatabase.commitAndGetAnnotations() @@ -54,7 +75,10 @@ private[chisel] object FloorplanDatabase { suggestion + s"_${id}" } - def commitAndGetAnnotations(): Seq[FloorplanModuleAnnotation] = elements.toSeq.map(_.getAnnotation()) + def commitAndGetAnnotations(): Seq[FloorplanModuleAnnotation] = { + elements.foreach(_.commit()) + elements.toSeq.map(_.getAnnotation()) + } } @@ -117,18 +141,18 @@ abstract class ChiselElement { final private var fpir: Element = null def getAnnotation(): FloorplanModuleAnnotation = { - if (!committed) this.commit() + if (!committed) throw ChiselFloorplanException("Must commit ChiselElement before getting its annotation!") FloorplanModuleAnnotation(module.toTarget, fpir) } final private[chisel] def commit(): Element = { - if (committed) throw new ChiselFloorplanException("Cannot commit floorplan more than once!") - committed = true - fpir = generateElement() + if (!committed) { + committed = true + fpir = generateElement() + } fpir } - FloorplanDatabase.register(module, this) } final class ChiselLogicRect private[chisel] ( @@ -140,7 +164,7 @@ final class ChiselLogicRect private[chisel] ( hardBoundary: Boolean ) extends ChiselElement { - val name = "top" // TODO better name for this + val name = module.getClass.getName.split('.').last protected def generateElement(): Element = ConstrainedLogicRect(name, width, height, area, aspectRatio, hardBoundary) @@ -148,14 +172,13 @@ final class ChiselLogicRect private[chisel] ( final class ChiselPlaceholderRect private[chisel] ( val module: RawModule, + val name: String, width: Constraint[LengthUnit] = Unconstrained[LengthUnit], height: Constraint[LengthUnit] = Unconstrained[LengthUnit], area: Constraint[AreaUnit] = Unconstrained[AreaUnit], aspectRatio: Constraint[Rational] = Unconstrained[Rational] ) extends ChiselElement { - val name = FloorplanDatabase.getUnusedName(module) - protected def generateElement(): Element = ConstrainedPlaceholderRect(name, width, height, area, aspectRatio) } @@ -179,11 +202,13 @@ final class ChiselWeightedGrid private[chisel] (val name: String, val module: Ra weights(x)(y) = weight } + def setModule(x: Int, y: Int, elementModule: RawModule, weight: Rational = Rational(1)): Unit = set(x, y, Floorplan.createRect(elementModule), weight) + protected def generateElement(): Element = WeightedGrid( name, xDim, yDim, - elements.flatten.map(_.getOrElse(new ChiselPlaceholderRect(module)).commit().name), + elements.flatten.map(_.getOrElse(Floorplan.createPlaceholderRect(module)).commit().name), weights.flatten, packed) diff --git a/floorplan/src/main/scala/firrtl/Passes.scala b/floorplan/src/main/scala/firrtl/Passes.scala index 036d1ede..975abc3b 100644 --- a/floorplan/src/main/scala/firrtl/Passes.scala +++ b/floorplan/src/main/scala/firrtl/Passes.scala @@ -23,13 +23,14 @@ class GenerateFloorplanIRPass extends Transform with RegisteredTransform { def execute(state: CircuitState): CircuitState = { // TODO don't need graph if there are no annos, which can be a speedup val graph = new InstanceGraph(state.circuit) - state.annotations.collect({ + val list = state.annotations.collect({ case x: FloorplanModuleAnnotation => graph.findInstancesInHierarchy(x.target.name). - map(_.tail.map(_.name)). - reduce(_ ++ _). - map(y => FloorplanElementRecord(Some(y), FloorplanSerialization.deserialize(x.fpir))) - }).flatten.foreach { list => + map(_.map(_.name).mkString(".")). + map(FloorplanElementRecord(_, FloorplanSerialization.deserialize(x.fpir))) + }).flatten + + if (list.nonEmpty) { val filename = state.annotations.collectFirst({ case x: FloorplanIRFileAnnotation => x.value }).getOrElse { @@ -37,7 +38,7 @@ class GenerateFloorplanIRPass extends Transform with RegisteredTransform { throw new Exception(s"Did not specify a filename for GenerateFloorplanIRPass. Please provide a FloorplanIRFileAnnotation or use the --${opt} option.") } val writer = new java.io.FileWriter(filename) - writer.write(FloorplanState.serialize(Seq(list))) + writer.write(FloorplanState.serialize(list)) writer.close() } state From 8e5706c7f56cd2ef687ff6297649292cf8226a2d Mon Sep 17 00:00:00 2001 From: John Wright Date: Tue, 5 Nov 2019 17:21:47 -0800 Subject: [PATCH 13/73] Clean up the Floorplan IR emission pass --- floorplan/src/main/scala/IR.scala | 9 ++-- floorplan/src/main/scala/chisel/API.scala | 48 +++++++++++-------- .../src/main/scala/firrtl/Annotations.scala | 16 +++++-- floorplan/src/main/scala/firrtl/Passes.scala | 25 ++++++++-- 4 files changed, 65 insertions(+), 33 deletions(-) diff --git a/floorplan/src/main/scala/IR.scala b/floorplan/src/main/scala/IR.scala index 72feb2be..a732dc59 100644 --- a/floorplan/src/main/scala/IR.scala +++ b/floorplan/src/main/scala/IR.scala @@ -7,8 +7,6 @@ package barstools.floorplan sealed abstract class Element { - val name: String - // TODO make this an Enumeration def level: Int def serialize = FloorplanSerialization.serialize(this) @@ -59,7 +57,6 @@ sealed trait LogicLike { } private[floorplan] final case class ConstrainedLogicRect( - name: String, width: Constraint[LengthUnit], height: Constraint[LengthUnit], area: Constraint[AreaUnit], @@ -73,7 +70,7 @@ sealed abstract class Group extends Element sealed abstract class Grid extends Group { val xDim: Int val yDim: Int - val elements: Seq[String] + val elements: Seq[(String, String)] assert(xDim > 0, "X dimension of grid must be positive") assert(yDim > 0, "Y dimension of grid must be positive") @@ -85,11 +82,13 @@ private[floorplan] final case class WeightedGrid( name: String, xDim: Int, yDim: Int, - elements: Seq[String], + elements: Seq[(String, String)], weights: Seq[Rational], packed: Boolean ) extends Grid { + def level = 1 + } // TODO add more layouts here diff --git a/floorplan/src/main/scala/chisel/API.scala b/floorplan/src/main/scala/chisel/API.scala index b6bd8e31..c95958e1 100644 --- a/floorplan/src/main/scala/chisel/API.scala +++ b/floorplan/src/main/scala/chisel/API.scala @@ -3,8 +3,10 @@ package barstools.floorplan.chisel import chisel3.{RawModule} +import firrtl.annotations.{InstanceTarget} + import barstools.floorplan._ -import barstools.floorplan.firrtl.FloorplanModuleAnnotation +import barstools.floorplan.firrtl.{FloorplanAnnotation, FloorplanInstanceAnnotation, FloorplanModuleAnnotation} import scala.collection.mutable.{ArraySeq, HashMap, Set, HashSet} final case class ChiselFloorplanException(message: String) extends Exception(message: String) @@ -47,7 +49,7 @@ object Floorplan { elt } - def commitAndGetAnnotations(): Seq[FloorplanModuleAnnotation] = FloorplanDatabase.commitAndGetAnnotations() + def commitAndGetAnnotations(): Seq[FloorplanAnnotation] = FloorplanDatabase.commitAndGetAnnotations() } @@ -75,7 +77,7 @@ private[chisel] object FloorplanDatabase { suggestion + s"_${id}" } - def commitAndGetAnnotations(): Seq[FloorplanModuleAnnotation] = { + def commitAndGetAnnotations(): Seq[FloorplanAnnotation] = { elements.foreach(_.commit()) elements.toSeq.map(_.getAnnotation()) } @@ -128,62 +130,66 @@ object FloorplanUnits { } -abstract class ChiselElement { - val module: RawModule - val name: String - final private def getName = name +abstract class ChiselElement(val module: RawModule, val name: String) { final private var committed = false protected def generateElement(): Element // TODO FIXME this is clunky + final protected def targetName: (String, String) = (s"${module.toAbsoluteTarget.serialize}", name) + + // TODO FIXME this is clunky too final private var fpir: Element = null - def getAnnotation(): FloorplanModuleAnnotation = { + def getAnnotation(): FloorplanAnnotation = { if (!committed) throw ChiselFloorplanException("Must commit ChiselElement before getting its annotation!") - FloorplanModuleAnnotation(module.toTarget, fpir) + FloorplanInstanceAnnotation(module.toAbsoluteTarget.asInstanceOf[InstanceTarget], fpir) } - final private[chisel] def commit(): Element = { + final private[chisel] def commit(): (String, String) = { if (!committed) { committed = true fpir = generateElement() } - fpir + targetName } } final class ChiselLogicRect private[chisel] ( - val module: RawModule, + module: RawModule, width: Constraint[LengthUnit], height: Constraint[LengthUnit], area: Constraint[AreaUnit], aspectRatio: Constraint[Rational], hardBoundary: Boolean -) extends ChiselElement { - - val name = module.getClass.getName.split('.').last +) extends ChiselElement(module, "") { - protected def generateElement(): Element = ConstrainedLogicRect(name, width, height, area, aspectRatio, hardBoundary) + protected def generateElement(): Element = ConstrainedLogicRect(width, height, area, aspectRatio, hardBoundary) } final class ChiselPlaceholderRect private[chisel] ( - val module: RawModule, - val name: String, + module: RawModule, + name: String, width: Constraint[LengthUnit] = Unconstrained[LengthUnit], height: Constraint[LengthUnit] = Unconstrained[LengthUnit], area: Constraint[AreaUnit] = Unconstrained[AreaUnit], aspectRatio: Constraint[Rational] = Unconstrained[Rational] -) extends ChiselElement { +) extends ChiselElement(module, name) { protected def generateElement(): Element = ConstrainedPlaceholderRect(name, width, height, area, aspectRatio) } -final class ChiselWeightedGrid private[chisel] (val name: String, val module: RawModule, x: Int, y: Int, packed: Boolean) extends ChiselElement { +final class ChiselWeightedGrid private[chisel] ( + name: String, + module: RawModule, + x: Int, + y: Int, + packed: Boolean +) extends ChiselElement(module, name) { assert(x > 0) assert(y > 0) @@ -208,7 +214,7 @@ final class ChiselWeightedGrid private[chisel] (val name: String, val module: Ra name, xDim, yDim, - elements.flatten.map(_.getOrElse(Floorplan.createPlaceholderRect(module)).commit().name), + elements.flatten.map(_.getOrElse(Floorplan.createPlaceholderRect(module)).commit()), weights.flatten, packed) diff --git a/floorplan/src/main/scala/firrtl/Annotations.scala b/floorplan/src/main/scala/firrtl/Annotations.scala index 6533e76a..5ab5af12 100644 --- a/floorplan/src/main/scala/firrtl/Annotations.scala +++ b/floorplan/src/main/scala/firrtl/Annotations.scala @@ -3,7 +3,7 @@ package barstools.floorplan.firrtl import barstools.floorplan.{Element} -import firrtl.annotations.{NoTargetAnnotation, SingleTargetAnnotation, Target, ModuleTarget, InstanceTarget, ReferenceTarget} +import firrtl.annotations.{Annotation, NoTargetAnnotation, SingleTargetAnnotation, Target, ModuleTarget, InstanceTarget, ReferenceTarget, IsModule} // John: Right now we are using ModuleTarget, which will mean that all instances of the same master // will have the same floorplan. This is probably OK for now, but eventually we will want to support @@ -12,14 +12,24 @@ import firrtl.annotations.{NoTargetAnnotation, SingleTargetAnnotation, Target, M // John: Another note. To make this a bit easier, I'm going to make floorplan IR embedded in this annotation rather than relying on // the annotation to serialize the case class correctly (it doesn't currently serialize type parameters, which makes this a bit painful) // We'll probably want to change this later -case class FloorplanModuleAnnotation(target: ModuleTarget, fpir: String) extends SingleTargetAnnotation[ModuleTarget] { +trait FloorplanAnnotation extends Annotation { + val fpir: String +} + +case class FloorplanModuleAnnotation(target: ModuleTarget, fpir: String) extends SingleTargetAnnotation[ModuleTarget] with FloorplanAnnotation { def duplicate(t: ModuleTarget) = this.copy(target, fpir) } -object FloorplanModuleAnnotation { +case class FloorplanInstanceAnnotation(target: InstanceTarget, fpir: String) extends SingleTargetAnnotation[InstanceTarget] with FloorplanAnnotation { + def duplicate(t: InstanceTarget) = this.copy(target, fpir) +} +object FloorplanModuleAnnotation { def apply(target: ModuleTarget, element: Element): FloorplanModuleAnnotation = FloorplanModuleAnnotation(target, element.serialize) +} +object FloorplanInstanceAnnotation { + def apply(target: InstanceTarget, element: Element): FloorplanInstanceAnnotation = FloorplanInstanceAnnotation(target, element.serialize) } case class FloorplanIRFileAnnotation(value: String) extends NoTargetAnnotation diff --git a/floorplan/src/main/scala/firrtl/Passes.scala b/floorplan/src/main/scala/firrtl/Passes.scala index 975abc3b..8929fd78 100644 --- a/floorplan/src/main/scala/firrtl/Passes.scala +++ b/floorplan/src/main/scala/firrtl/Passes.scala @@ -5,6 +5,7 @@ import barstools.floorplan.{FloorplanSerialization, FloorplanElementRecord, Floo import firrtl.{CircuitState, LowForm, Namespace, Transform, AnnotationSeq} import firrtl.options.{RegisteredTransform, ShellOption} import firrtl.analyses.{InstanceGraph} +import firrtl.annotations.{InstanceTarget, ModuleTarget} // NOTE: If you rename/add this transform, don't forget to update META-INF // See the @note in the RegisteredTransform documentation @@ -20,14 +21,30 @@ class GenerateFloorplanIRPass extends Transform with RegisteredTransform { ) ) + + private def getInstancePathsFromGraph(graph: InstanceGraph, cktName: String, name: String): Seq[String] = { + if (cktName == name) { + return Seq("") + } else { + return graph.findInstancesInHierarchy(name).map(_.map(_.name).mkString(".") + ".") + } + } + + def execute(state: CircuitState): CircuitState = { // TODO don't need graph if there are no annos, which can be a speedup val graph = new InstanceGraph(state.circuit) + + def getPaths(name: String) = getInstancePathsFromGraph(graph, state.circuit.main, name) + + def newRecord(path: String, anno: FloorplanAnnotation) = FloorplanElementRecord(path, FloorplanSerialization.deserialize(anno.fpir)) + val list = state.annotations.collect({ - case x: FloorplanModuleAnnotation => - graph.findInstancesInHierarchy(x.target.name). - map(_.map(_.name).mkString(".")). - map(FloorplanElementRecord(_, FloorplanSerialization.deserialize(x.fpir))) + case x: FloorplanInstanceAnnotation => { + val t = x.target + getPaths(t.module).map(_ + (t.path.toList.map(_._1.value) :+ t.instance mkString ".")).map(newRecord(_, x)) + } + case x: FloorplanModuleAnnotation => getPaths(x.target.name).map(newRecord(_, x)) }).flatten if (list.nonEmpty) { From 7021d0c3fd29e2a97714490e2a232be0718690a5 Mon Sep 17 00:00:00 2001 From: John Wright Date: Wed, 6 Nov 2019 18:35:34 -0800 Subject: [PATCH 14/73] Make the MultiTargetAnnotation stuff work; TODO: get rid of some of the asInstanceOf's --- floorplan/src/main/scala/IR.scala | 14 ++- floorplan/src/main/scala/chisel/API.scala | 85 +++++++++++++------ .../src/main/scala/firrtl/Annotations.scala | 14 ++- floorplan/src/main/scala/firrtl/Passes.scala | 23 +++-- 4 files changed, 100 insertions(+), 36 deletions(-) diff --git a/floorplan/src/main/scala/IR.scala b/floorplan/src/main/scala/IR.scala index a732dc59..8309f1d5 100644 --- a/floorplan/src/main/scala/IR.scala +++ b/floorplan/src/main/scala/IR.scala @@ -65,12 +65,18 @@ private[floorplan] final case class ConstrainedLogicRect( private[floorplan] final case class ConcreteLogicRect(name: String, width: LengthUnit, height: LengthUnit, hardBoundary: Boolean) extends ConcreteRect with LogicLike -sealed abstract class Group extends Element +sealed abstract class Group extends Element { + + val elements: Seq[String] + + def mapElements(f: ((String, Int)) => String): Group + +} sealed abstract class Grid extends Group { val xDim: Int val yDim: Int - val elements: Seq[(String, String)] + val elements: Seq[String] assert(xDim > 0, "X dimension of grid must be positive") assert(yDim > 0, "Y dimension of grid must be positive") @@ -82,13 +88,15 @@ private[floorplan] final case class WeightedGrid( name: String, xDim: Int, yDim: Int, - elements: Seq[(String, String)], + elements: Seq[String], weights: Seq[Rational], packed: Boolean ) extends Grid { def level = 1 + def mapElements(f: ((String, Int)) => String) = this.copy(name, xDim, yDim, elements.zipWithIndex.map(f), weights, packed) + } // TODO add more layouts here diff --git a/floorplan/src/main/scala/chisel/API.scala b/floorplan/src/main/scala/chisel/API.scala index c95958e1..169c70e6 100644 --- a/floorplan/src/main/scala/chisel/API.scala +++ b/floorplan/src/main/scala/chisel/API.scala @@ -3,10 +3,10 @@ package barstools.floorplan.chisel import chisel3.{RawModule} -import firrtl.annotations.{InstanceTarget} +import firrtl.annotations.{ModuleTarget, InstanceTarget} import barstools.floorplan._ -import barstools.floorplan.firrtl.{FloorplanAnnotation, FloorplanInstanceAnnotation, FloorplanModuleAnnotation} +import barstools.floorplan.firrtl.{FloorplanAnnotation, FloorplanInstanceAnnotation, FloorplanModuleAnnotation, FloorplanGroupAnnotation} import scala.collection.mutable.{ArraySeq, HashMap, Set, HashSet} final case class ChiselFloorplanException(message: String) extends Exception(message: String) @@ -137,22 +137,51 @@ abstract class ChiselElement(val module: RawModule, val name: String) { protected def generateElement(): Element // TODO FIXME this is clunky - final protected def targetName: (String, String) = (s"${module.toAbsoluteTarget.serialize}", name) + //final protected def targetName: (String, String) = (s"${module.toAbsoluteTarget.serialize}", name) // TODO FIXME this is clunky too - final private var fpir: Element = null + final protected var fpir: Element = null - def getAnnotation(): FloorplanAnnotation = { - if (!committed) throw ChiselFloorplanException("Must commit ChiselElement before getting its annotation!") - FloorplanInstanceAnnotation(module.toAbsoluteTarget.asInstanceOf[InstanceTarget], fpir) - } + def getAnnotation(): FloorplanAnnotation + + def isCommitted = committed - final private[chisel] def commit(): (String, String) = { - if (!committed) { + final private[chisel] def commit(): String = { + if (!isCommitted) { committed = true fpir = generateElement() } - targetName + name + } + +} + +abstract class ChiselPrimitiveElement(module: RawModule, name: String) extends ChiselElement(module, name) { + + def getAnnotation(): FloorplanAnnotation = { + if (!isCommitted) throw ChiselFloorplanException("Must commit ChiselElement before getting its annotation!") + module.toAbsoluteTarget match { + case x: InstanceTarget => FloorplanInstanceAnnotation(x, fpir) + case x: ModuleTarget => FloorplanModuleAnnotation(x, fpir) + case _ => ??? + } + } + +} + +abstract class ChiselGroupElement(module: RawModule, name: String) extends ChiselElement(module, name) { + + protected val elements: Seq[Option[ChiselElement]] + + def getAnnotation(): FloorplanAnnotation = { + if (!isCommitted) throw ChiselFloorplanException("Must commit ChiselElement before getting its annotation!") + // The head of elements will be this module, while the remaining ones are the children + val elementTargets = this.module.toAbsoluteTarget +: elements.map(_.get.module.toAbsoluteTarget) + val elementTargetsMapped: Seq[Seq[InstanceTarget]] = elementTargets.map { x => + assert(x.isInstanceOf[InstanceTarget], "All targets must be InstanceTargets for ChiselGroupElement annotations") + Seq(x.asInstanceOf[InstanceTarget]) + } + FloorplanGroupAnnotation(elementTargetsMapped, fpir.asInstanceOf[Group]) } } @@ -164,7 +193,7 @@ final class ChiselLogicRect private[chisel] ( area: Constraint[AreaUnit], aspectRatio: Constraint[Rational], hardBoundary: Boolean -) extends ChiselElement(module, "") { +) extends ChiselPrimitiveElement(module, "") { protected def generateElement(): Element = ConstrainedLogicRect(width, height, area, aspectRatio, hardBoundary) @@ -177,7 +206,7 @@ final class ChiselPlaceholderRect private[chisel] ( height: Constraint[LengthUnit] = Unconstrained[LengthUnit], area: Constraint[AreaUnit] = Unconstrained[AreaUnit], aspectRatio: Constraint[Rational] = Unconstrained[Rational] -) extends ChiselElement(module, name) { +) extends ChiselPrimitiveElement(module, name) { protected def generateElement(): Element = ConstrainedPlaceholderRect(name, width, height, area, aspectRatio) @@ -189,7 +218,7 @@ final class ChiselWeightedGrid private[chisel] ( x: Int, y: Int, packed: Boolean -) extends ChiselElement(module, name) { +) extends ChiselGroupElement(module, name) { assert(x > 0) assert(y > 0) @@ -198,24 +227,30 @@ final class ChiselWeightedGrid private[chisel] ( private var xDim = x private var yDim = y - private val elements = ArraySeq.fill[Option[ChiselElement]](xDim, yDim) { Option.empty[ChiselElement] } - private val weights = ArraySeq.fill[Rational](xDim, yDim) { Rational(1) } + protected val elements = ArraySeq.fill[Option[ChiselElement]](xDim*yDim) { Option.empty[ChiselElement] } + private val weights = ArraySeq.fill[Rational](xDim*yDim) { Rational(1) } def set(x: Int, y: Int, element: ChiselElement, weight: Rational = Rational(1)): Unit = { + if (isCommitted) throw new ChiselFloorplanException("Cannot modify a ChiselWeightedGrid after committing") if (x >= xDim) throw new IndexOutOfBoundsException(s"X-value ${x} >= ${xDim} in ChiselWeightedGrid") if (y >= yDim) throw new IndexOutOfBoundsException(s"Y-value ${y} >= ${yDim} in ChiselWeightedGrid") - elements(x)(y) = Some(element) - weights(x)(y) = weight + elements(y*xDim + x) = Some(element) + weights(y*xDim + x) = weight } def setModule(x: Int, y: Int, elementModule: RawModule, weight: Rational = Rational(1)): Unit = set(x, y, Floorplan.createRect(elementModule), weight) - protected def generateElement(): Element = WeightedGrid( - name, - xDim, - yDim, - elements.flatten.map(_.getOrElse(Floorplan.createPlaceholderRect(module)).commit()), - weights.flatten, - packed) + private def convertNonesToPlaceholders() = elements.transform(_.orElse(Some(Floorplan.createPlaceholderRect(module)))) + + protected def generateElement(): Element = { + convertNonesToPlaceholders() + WeightedGrid( + name, + xDim, + yDim, + elements.map(_.get.commit()), + weights, + packed) + } } diff --git a/floorplan/src/main/scala/firrtl/Annotations.scala b/floorplan/src/main/scala/firrtl/Annotations.scala index 5ab5af12..803d36f0 100644 --- a/floorplan/src/main/scala/firrtl/Annotations.scala +++ b/floorplan/src/main/scala/firrtl/Annotations.scala @@ -2,8 +2,8 @@ package barstools.floorplan.firrtl -import barstools.floorplan.{Element} -import firrtl.annotations.{Annotation, NoTargetAnnotation, SingleTargetAnnotation, Target, ModuleTarget, InstanceTarget, ReferenceTarget, IsModule} +import barstools.floorplan.{Element, Group} +import firrtl.annotations.{Annotation, NoTargetAnnotation, SingleTargetAnnotation, MultiTargetAnnotation, Target, ModuleTarget, InstanceTarget, ReferenceTarget, IsModule} // John: Right now we are using ModuleTarget, which will mean that all instances of the same master // will have the same floorplan. This is probably OK for now, but eventually we will want to support @@ -24,6 +24,10 @@ case class FloorplanInstanceAnnotation(target: InstanceTarget, fpir: String) ext def duplicate(t: InstanceTarget) = this.copy(target, fpir) } +case class FloorplanGroupAnnotation(targets: Seq[Seq[Target]], fpir: String) extends MultiTargetAnnotation with FloorplanAnnotation { + def duplicate(t: Seq[Seq[Target]]) = this.copy(targets, fpir) +} + object FloorplanModuleAnnotation { def apply(target: ModuleTarget, element: Element): FloorplanModuleAnnotation = FloorplanModuleAnnotation(target, element.serialize) } @@ -32,4 +36,10 @@ object FloorplanInstanceAnnotation { def apply(target: InstanceTarget, element: Element): FloorplanInstanceAnnotation = FloorplanInstanceAnnotation(target, element.serialize) } +object FloorplanGroupAnnotation { + //def apply(targets: Seq[Target], fpir: String): FloorplanGroupAnnotation = FloorplanGroupAnnotation(targets.map(Seq(_)), fpir) + //def apply(targets: Seq[Target], element: Element): FloorplanGroupAnnotation = FloorplanGroupAnnotation(targets, element.serialize) + def apply(targets: Seq[Seq[Target]], element: Group): FloorplanGroupAnnotation = FloorplanGroupAnnotation(targets, element.serialize) +} + case class FloorplanIRFileAnnotation(value: String) extends NoTargetAnnotation diff --git a/floorplan/src/main/scala/firrtl/Passes.scala b/floorplan/src/main/scala/firrtl/Passes.scala index 8929fd78..f17087e7 100644 --- a/floorplan/src/main/scala/firrtl/Passes.scala +++ b/floorplan/src/main/scala/firrtl/Passes.scala @@ -35,16 +35,27 @@ class GenerateFloorplanIRPass extends Transform with RegisteredTransform { // TODO don't need graph if there are no annos, which can be a speedup val graph = new InstanceGraph(state.circuit) - def getPaths(name: String) = getInstancePathsFromGraph(graph, state.circuit.main, name) - + def getPaths(name: String): Seq[String] = getInstancePathsFromGraph(graph, state.circuit.main, name) + def getInstancePath(t: InstanceTarget): String = { + val result = getPaths(t.module).map(_ + (t.path.toList.map(_._1.value) :+ t.instance mkString ".")) + if (result.size > 1) throw new Exception(s"Too many instances for InstanceTarget ${t}! Fix me!") + if (result.size == 0) throw new Exception(s"InstanceTarget ${t} does not exist!") + result(0) + } def newRecord(path: String, anno: FloorplanAnnotation) = FloorplanElementRecord(path, FloorplanSerialization.deserialize(anno.fpir)) val list = state.annotations.collect({ - case x: FloorplanInstanceAnnotation => { - val t = x.target - getPaths(t.module).map(_ + (t.path.toList.map(_._1.value) :+ t.instance mkString ".")).map(newRecord(_, x)) - } + case x: FloorplanInstanceAnnotation => Seq(newRecord(getInstancePath(x.target), x)) case x: FloorplanModuleAnnotation => getPaths(x.target.name).map(newRecord(_, x)) + case x: FloorplanGroupAnnotation => { + val paths = x.targets.map(_(0).asInstanceOf[InstanceTarget]).map(getInstancePath) + // paths(0) is special; it's the path to the module the element is attached to + val element = FloorplanSerialization. + deserialize(x.fpir). + asInstanceOf[barstools.floorplan.Group]. + mapElements { case (name, id) => paths(id + 1) + "#" + name } + Seq(FloorplanElementRecord(paths(0), element)) + } }).flatten if (list.nonEmpty) { From 7eb69913a5272e0db05b015d8a03acb1c6943259 Mon Sep 17 00:00:00 2001 From: John Wright Date: Thu, 7 Nov 2019 10:55:50 -0800 Subject: [PATCH 15/73] Start adding compiler infrastructure --- floorplan/src/main/scala/FloorplanState.scala | 17 ++++ floorplan/src/main/scala/Serialization.scala | 21 +---- .../scala/compiler/FloorplanCompiler.scala | 3 +- .../main/scala/compiler/FloorplanTree.scala | 80 +++++++++++++++++++ .../src/main/scala/compiler/Passes.scala | 4 +- 5 files changed, 104 insertions(+), 21 deletions(-) create mode 100644 floorplan/src/main/scala/FloorplanState.scala create mode 100644 floorplan/src/main/scala/compiler/FloorplanTree.scala diff --git a/floorplan/src/main/scala/FloorplanState.scala b/floorplan/src/main/scala/FloorplanState.scala new file mode 100644 index 00000000..a032185f --- /dev/null +++ b/floorplan/src/main/scala/FloorplanState.scala @@ -0,0 +1,17 @@ +// See LICENSE for license details +package barstools.floorplan + +final case class FloorplanElementRecord(path: String, element: Element) + +final case class FloorplanState(elements: Seq[FloorplanElementRecord], level: Int) + +object FloorplanState { + + def fromSeq(seq: Seq[FloorplanElementRecord]): FloorplanState = FloorplanState(seq, seq.map(_.element.level).max) + + def serialize(state: FloorplanState): String = FloorplanSerialization.serializeState(state) + def serialize(seq: Seq[FloorplanElementRecord]): String = serialize(fromSeq(seq)) + + def deserialize(str: String): FloorplanState = FloorplanSerialization.deserializeState(str) + +} diff --git a/floorplan/src/main/scala/Serialization.scala b/floorplan/src/main/scala/Serialization.scala index ee1ecf79..3b40825e 100644 --- a/floorplan/src/main/scala/Serialization.scala +++ b/floorplan/src/main/scala/Serialization.scala @@ -5,14 +5,6 @@ import org.json4s._ import org.json4s.native.Serialization.{read, write, writePretty} import scala.reflect.runtime.universe.typeOf -final case class FloorplanElementRecord(path: String, element: Element) - -final case class FloorplanState(elements: Seq[FloorplanElementRecord], level: Int) { - - def generateDB: FloorplanState.Database = ??? - -} - object FloorplanSerialization { // Because Element is sealed, all of its subclasses are known at compile time, so we can construct type hints for them @@ -37,18 +29,9 @@ object FloorplanSerialization { read[Element](str) } -} - -object FloorplanState { - - type Database = Map[String, Element] - - def fromSeq(seq: Seq[FloorplanElementRecord]): FloorplanState = FloorplanState(seq, seq.map(_.element.level).max) - - def serialize(state: FloorplanState): String = writePretty(state)(FloorplanSerialization.formats) - def serialize(seq: Seq[FloorplanElementRecord]): String = serialize(fromSeq(seq)) + def serializeState(state: FloorplanState): String = writePretty(state)(formats) - def deserialize(str: String): FloorplanState = { + def deserializeState(str: String): FloorplanState = { implicit val formats = FloorplanSerialization.formats read[FloorplanState](str) } diff --git a/floorplan/src/main/scala/compiler/FloorplanCompiler.scala b/floorplan/src/main/scala/compiler/FloorplanCompiler.scala index 28e73781..913e1d9f 100644 --- a/floorplan/src/main/scala/compiler/FloorplanCompiler.scala +++ b/floorplan/src/main/scala/compiler/FloorplanCompiler.scala @@ -28,5 +28,6 @@ object FloorplanCompiler extends App { throw new Exception("Error parsing options!") } - println(opts.inputFloorplanFile) + + } diff --git a/floorplan/src/main/scala/compiler/FloorplanTree.scala b/floorplan/src/main/scala/compiler/FloorplanTree.scala new file mode 100644 index 00000000..804d6813 --- /dev/null +++ b/floorplan/src/main/scala/compiler/FloorplanTree.scala @@ -0,0 +1,80 @@ +// See LICENSE for license details +package barstools.floorplan.compiler + +import scala.collection.mutable.{HashMap} +import barstools.floorplan.{Element, FloorplanState} + +class FloorplanTree(state: FloorplanState) { + + class Node(val parent: Option[Node], val name: String) { + val children = new HashMap[String, Node]() + val elements = new HashMap[String, Element]() + + def addElement(path: Seq[String], tag: String, element: Element): Unit = { + if (path.isEmpty) { + if (elements.contains(tag)) { + throw new Exception(s"Duplicate tag ${tag} at floorplan path ${getPath}") + } else { + elements.update(tag, element) + } + } else { + val child = children.getOrElseUpdate(path.head, new Node(Some(this), path.head)) + child.addElement(path.tail, tag, element) + } + } + + def getPathHelper(sub: String): String = parent.map(_.getPathHelper(s"${name}.${sub}")).getOrElse(sub) + + def getPath: String = getPathHelper(name) + + def getNode(path: Seq[String]): Node = { + if (path.isEmpty) { + this + } else { + children.get(path.head).map(_.getNode(path.tail)).getOrElse { + throw new Exception(s"Path ${getPath}.${path} does not exist in tree") + } + } + } + + def getElement(tag: String): Element = { + elements.get(tag).getOrElse { throw new Exception(s"Tag ${tag} does not exist in node ${getPath}") } + } + + def getElement(path: Seq[String], tag: String): Element = (if (path.isEmpty) this else getNode(path)).getElement(tag) + } + + val root = new Node(None, "") + + private def parsePathAndTag(str: String): (Seq[String], String) = { + val split = str.split("#", -1) + assert(split.size == 2, s"Malformed floorplan record path: ${str}") + val path = split(0).split(".") + val tag = split(1) + (path, tag) + } + + state.elements.foreach { record => + val (path, tag) = parsePathAndTag(record.path) + root.addElement(path, tag, record.element) + } + + def getNode(path: Seq[String]): Node = root.getNode(path) + + def getNode(str: String): Node = { + if (str.contains("#")) throw new Exception(s"getNode cannot be called on an element path (containing #): ${str}") + getNode(str.split(".")) + } + + def getElement(str: String): Element = { + val (path, tag) = parsePathAndTag(str) + root.getElement(path, tag) + } + +} + +object FloorplanTree { + + def apply(state: FloorplanState) = new FloorplanTree(state) + +} diff --git a/floorplan/src/main/scala/compiler/Passes.scala b/floorplan/src/main/scala/compiler/Passes.scala index 51f00ad7..a0daf1f8 100644 --- a/floorplan/src/main/scala/compiler/Passes.scala +++ b/floorplan/src/main/scala/compiler/Passes.scala @@ -24,7 +24,9 @@ class MacroLayoutsPass { class BottomUpPropagationPass { - def execute(state: FloorplanState): FloorplanState = ??? + def execute(state: FloorplanState): FloorplanState = { + ??? + } } From 87c62ba98caf3c86548f3f026c37d941ef044910 Mon Sep 17 00:00:00 2001 From: John Wright Date: Thu, 7 Nov 2019 12:17:26 -0800 Subject: [PATCH 16/73] Add Layouts --- floorplan/src/main/scala/Constraints.scala | 15 +++++ floorplan/src/main/scala/IR.scala | 64 ++++++++++++++------ floorplan/src/main/scala/Serialization.scala | 1 + floorplan/src/main/scala/chisel/API.scala | 62 ++++++++++++++----- 4 files changed, 109 insertions(+), 33 deletions(-) diff --git a/floorplan/src/main/scala/Constraints.scala b/floorplan/src/main/scala/Constraints.scala index d18fa117..68ed2b23 100644 --- a/floorplan/src/main/scala/Constraints.scala +++ b/floorplan/src/main/scala/Constraints.scala @@ -427,3 +427,18 @@ object AndConstraint { def apply[T <: HasUnit](a: Constraint[T], b: Constraint[T]): AndConstraint[T] = AndConstraint(Seq(a, b)) } + +sealed trait RelativePlacementAnchor + +case object LowerLeft extends RelativePlacementAnchor +case object LowerMiddle extends RelativePlacementAnchor +case object LowerRight extends RelativePlacementAnchor +case object CenterLeft extends RelativePlacementAnchor +case object CenterMiddle extends RelativePlacementAnchor +case object CenterRight extends RelativePlacementAnchor +case object UpperLeft extends RelativePlacementAnchor +case object UpperMiddle extends RelativePlacementAnchor +case object UpperRight extends RelativePlacementAnchor + +final case class RelativePlacementConstraint(value: Constraint[Rational], anchor: RelativePlacementAnchor) + diff --git a/floorplan/src/main/scala/IR.scala b/floorplan/src/main/scala/IR.scala index 8309f1d5..6e63524f 100644 --- a/floorplan/src/main/scala/IR.scala +++ b/floorplan/src/main/scala/IR.scala @@ -17,26 +17,28 @@ sealed abstract class Primitive extends Element ////////////////////////////////////////////// Rect shape -sealed abstract class AbstractRect extends Primitive { - final def level = 2 -} - -sealed abstract class ConstrainedRect extends Primitive { - final def level = 1 - +trait ConstrainedRectLike { val width: Constraint[LengthUnit] val height: Constraint[LengthUnit] val area: Constraint[AreaUnit] val aspectRatio: Constraint[Rational] - } -sealed abstract class ConcreteRect extends Primitive { - final def level = 0 - +trait ConcreteRectLike { val width: LengthUnit val height: LengthUnit +} + +sealed abstract class AbstractRectPrimitive extends Primitive { + final def level = 2 +} + +sealed abstract class ConstrainedRectPrimitive extends Primitive with ConstrainedRectLike { + final def level = 1 +} +sealed abstract class ConcreteRectPrimitive extends Primitive with ConcreteRectLike { + final def level = 0 } private[floorplan] final case class ConstrainedPlaceholderRect( @@ -44,16 +46,18 @@ private[floorplan] final case class ConstrainedPlaceholderRect( width: Constraint[LengthUnit] = Unconstrained[LengthUnit], height: Constraint[LengthUnit] = Unconstrained[LengthUnit], area: Constraint[AreaUnit] = Unconstrained[AreaUnit], - aspectRatio: Constraint[Rational] = Unconstrained[Rational]) extends ConstrainedRect + aspectRatio: Constraint[Rational] = Unconstrained[Rational] +) extends ConstrainedRectPrimitive sealed trait MacroLike -private[floorplan] final case class AbstractMacro(name: String) extends AbstractRect with MacroLike +private[floorplan] final case class AbstractMacro(name: String) extends AbstractRectPrimitive with MacroLike -private[floorplan] final case class ConcreteMacro(name: String, width: LengthUnit, height: LengthUnit) extends ConcreteRect with MacroLike +private[floorplan] final case class ConcreteMacro(name: String, width: LengthUnit, height: LengthUnit) extends ConcreteRectPrimitive with MacroLike -sealed trait LogicLike { - val hardBoundary: Boolean +sealed trait IsModuleTop { + // Basically prevent subclasses from implementing name + final def name = "" } private[floorplan] final case class ConstrainedLogicRect( @@ -61,9 +65,10 @@ private[floorplan] final case class ConstrainedLogicRect( height: Constraint[LengthUnit], area: Constraint[AreaUnit], aspectRatio: Constraint[Rational], - hardBoundary: Boolean) extends ConstrainedRect with LogicLike + hardBoundary: Boolean +) extends ConstrainedRectPrimitive with IsModuleTop -private[floorplan] final case class ConcreteLogicRect(name: String, width: LengthUnit, height: LengthUnit, hardBoundary: Boolean) extends ConcreteRect with LogicLike +private[floorplan] final case class ConcreteLogicRect(width: LengthUnit, height: LengthUnit, hardBoundary: Boolean) extends ConcreteRectPrimitive with IsModuleTop sealed abstract class Group extends Element { @@ -71,6 +76,8 @@ sealed abstract class Group extends Element { def mapElements(f: ((String, Int)) => String): Group + protected def mapElementsHelper(f: ((String, Int)) => String): Seq[String] = elements.zipWithIndex.map(f) + } sealed abstract class Grid extends Group { @@ -95,7 +102,26 @@ private[floorplan] final case class WeightedGrid( def level = 1 - def mapElements(f: ((String, Int)) => String) = this.copy(name, xDim, yDim, elements.zipWithIndex.map(f), weights, packed) + def mapElements(f: ((String, Int)) => String) = this.copy(name, xDim, yDim, mapElementsHelper(f), weights, packed) + +} + +sealed abstract class Layout extends Group with IsModuleTop +sealed abstract class ConstrainedLayout extends Layout with ConstrainedRectLike +sealed abstract class ConcreteLayout extends Layout with ConcreteRectLike + +private[floorplan] final case class ConstrainedRelativePlacement( + elements: Seq[String], + placements: Seq[RelativePlacementConstraint], + width: Constraint[LengthUnit] = Unconstrained[LengthUnit], + height: Constraint[LengthUnit] = Unconstrained[LengthUnit], + area: Constraint[AreaUnit] = Unconstrained[AreaUnit], + aspectRatio: Constraint[Rational] = Unconstrained[Rational] +) extends ConstrainedLayout { + + def level = 1 + + def mapElements(f: ((String, Int)) => String) = this.copy(mapElementsHelper(f), placements, width, height, area, aspectRatio) } diff --git a/floorplan/src/main/scala/Serialization.scala b/floorplan/src/main/scala/Serialization.scala index 3b40825e..0a715c08 100644 --- a/floorplan/src/main/scala/Serialization.scala +++ b/floorplan/src/main/scala/Serialization.scala @@ -11,6 +11,7 @@ object FloorplanSerialization { private val typeHintClasses = Seq( typeOf[Element], typeOf[Unit], + typeOf[RelativePlacementAnchor], typeOf[Constraint[Rational]], typeOf[Constraint[LengthUnit]], typeOf[Constraint[AreaUnit]], diff --git a/floorplan/src/main/scala/chisel/API.scala b/floorplan/src/main/scala/chisel/API.scala index 169c70e6..31b27aed 100644 --- a/floorplan/src/main/scala/chisel/API.scala +++ b/floorplan/src/main/scala/chisel/API.scala @@ -44,7 +44,7 @@ object Floorplan { y: Int = 1, packed: Boolean = false ) = { - val elt = new ChiselWeightedGrid(name, module, x, y, packed) + val elt = new ChiselWeightedGrid(module, name, x, y, packed) FloorplanDatabase.register(module, elt) elt } @@ -186,13 +186,15 @@ abstract class ChiselGroupElement(module: RawModule, name: String) extends Chise } +abstract class ChiselLayoutElement(module: RawModule) extends ChiselGroupElement(module, "") + final class ChiselLogicRect private[chisel] ( module: RawModule, - width: Constraint[LengthUnit], - height: Constraint[LengthUnit], - area: Constraint[AreaUnit], - aspectRatio: Constraint[Rational], - hardBoundary: Boolean + val width: Constraint[LengthUnit], + val height: Constraint[LengthUnit], + val area: Constraint[AreaUnit], + val aspectRatio: Constraint[Rational], + val hardBoundary: Boolean ) extends ChiselPrimitiveElement(module, "") { protected def generateElement(): Element = ConstrainedLogicRect(width, height, area, aspectRatio, hardBoundary) @@ -202,10 +204,10 @@ final class ChiselLogicRect private[chisel] ( final class ChiselPlaceholderRect private[chisel] ( module: RawModule, name: String, - width: Constraint[LengthUnit] = Unconstrained[LengthUnit], - height: Constraint[LengthUnit] = Unconstrained[LengthUnit], - area: Constraint[AreaUnit] = Unconstrained[AreaUnit], - aspectRatio: Constraint[Rational] = Unconstrained[Rational] + val width: Constraint[LengthUnit] = Unconstrained[LengthUnit], + val height: Constraint[LengthUnit] = Unconstrained[LengthUnit], + val area: Constraint[AreaUnit] = Unconstrained[AreaUnit], + val aspectRatio: Constraint[Rational] = Unconstrained[Rational] ) extends ChiselPrimitiveElement(module, name) { protected def generateElement(): Element = ConstrainedPlaceholderRect(name, width, height, area, aspectRatio) @@ -213,11 +215,11 @@ final class ChiselPlaceholderRect private[chisel] ( } final class ChiselWeightedGrid private[chisel] ( - name: String, module: RawModule, - x: Int, - y: Int, - packed: Boolean + name: String, + val x: Int, + val y: Int, + val packed: Boolean ) extends ChiselGroupElement(module, name) { assert(x > 0) @@ -254,3 +256,35 @@ final class ChiselWeightedGrid private[chisel] ( } } + + +final class ChiselConstrainedRelativePlacement private[chisel] ( + module: RawModule, + val width: Constraint[LengthUnit] = Unconstrained[LengthUnit], + val height: Constraint[LengthUnit] = Unconstrained[LengthUnit], + val area: Constraint[AreaUnit] = Unconstrained[AreaUnit], + val aspectRatio: Constraint[Rational] = Unconstrained[Rational] +) extends ChiselLayoutElement(module) { + + protected val elements = new ArraySeq[Option[ChiselElement]](0) + protected val placements = new ArraySeq[RelativePlacementConstraint](0) + + def add(element: ChiselElement, constraint: RelativePlacementConstraint): Unit = { + if (isCommitted) throw new ChiselFloorplanException("Cannot modify a ChiselConstrainedRelativePlacement after committing") + elements :+ Some(element) + placements :+ constraint + } + + def addModule(elementModule: RawModule, constraint: RelativePlacementConstraint): Unit = add(Floorplan.createRect(elementModule), constraint) + + protected def generateElement(): Element = { + ConstrainedRelativePlacement( + elements.map(_.get.commit()), + placements, + width, + height, + area, + aspectRatio) + } + +} From 7d3de4500fb7d3682d066c78bd1cc177309c68c1 Mon Sep 17 00:00:00 2001 From: John Wright Date: Fri, 8 Nov 2019 10:50:50 -0800 Subject: [PATCH 17/73] Rename relative placement to ratio, add length layout --- floorplan/src/main/scala/Constraints.scala | 37 ++++++--- floorplan/src/main/scala/IR.scala | 19 ++++- floorplan/src/main/scala/Serialization.scala | 2 +- floorplan/src/main/scala/chisel/API.scala | 79 ++++++++++++++++--- .../scala/compiler/FloorplanCompiler.scala | 2 - .../main/scala/compiler/FloorplanTree.scala | 22 +++++- .../src/main/scala/compiler/Passes.scala | 1 + 7 files changed, 133 insertions(+), 29 deletions(-) diff --git a/floorplan/src/main/scala/Constraints.scala b/floorplan/src/main/scala/Constraints.scala index 68ed2b23..3fc303b4 100644 --- a/floorplan/src/main/scala/Constraints.scala +++ b/floorplan/src/main/scala/Constraints.scala @@ -428,17 +428,28 @@ object AndConstraint { } -sealed trait RelativePlacementAnchor - -case object LowerLeft extends RelativePlacementAnchor -case object LowerMiddle extends RelativePlacementAnchor -case object LowerRight extends RelativePlacementAnchor -case object CenterLeft extends RelativePlacementAnchor -case object CenterMiddle extends RelativePlacementAnchor -case object CenterRight extends RelativePlacementAnchor -case object UpperLeft extends RelativePlacementAnchor -case object UpperMiddle extends RelativePlacementAnchor -case object UpperRight extends RelativePlacementAnchor - -final case class RelativePlacementConstraint(value: Constraint[Rational], anchor: RelativePlacementAnchor) +sealed trait PlacementAnchor + +class LowerLeft extends PlacementAnchor +class LowerMiddle extends PlacementAnchor +class LowerRight extends PlacementAnchor +class CenterLeft extends PlacementAnchor +class CenterMiddle extends PlacementAnchor +class CenterRight extends PlacementAnchor +class UpperLeft extends PlacementAnchor +class UpperMiddle extends PlacementAnchor +class UpperRight extends PlacementAnchor + +object LowerLeft { def apply() = new LowerLeft } +object LowerMiddle { def apply() = new LowerMiddle } +object LowerRight { def apply() = new LowerRight } +object CenterLeft { def apply() = new CenterLeft } +object CenterMiddle { def apply() = new CenterMiddle } +object CenterRight { def apply() = new CenterRight } +object UpperLeft { def apply() = new UpperLeft } +object UpperMiddle { def apply() = new UpperMiddle } +object UpperRight { def apply() = new UpperRight } + +final case class RatioPlacementConstraint(x: Constraint[Rational], y: Constraint[Rational], anchor: PlacementAnchor = LowerLeft()) +final case class LengthPlacementConstraint(x: Constraint[LengthUnit], y: Constraint[LengthUnit], anchor: PlacementAnchor = LowerLeft()) diff --git a/floorplan/src/main/scala/IR.scala b/floorplan/src/main/scala/IR.scala index 6e63524f..ca7762a5 100644 --- a/floorplan/src/main/scala/IR.scala +++ b/floorplan/src/main/scala/IR.scala @@ -110,9 +110,24 @@ sealed abstract class Layout extends Group with IsModuleTop sealed abstract class ConstrainedLayout extends Layout with ConstrainedRectLike sealed abstract class ConcreteLayout extends Layout with ConcreteRectLike -private[floorplan] final case class ConstrainedRelativePlacement( +private[floorplan] final case class ConstrainedRatioLayout( elements: Seq[String], - placements: Seq[RelativePlacementConstraint], + placements: Seq[RatioPlacementConstraint], + width: Constraint[LengthUnit] = Unconstrained[LengthUnit], + height: Constraint[LengthUnit] = Unconstrained[LengthUnit], + area: Constraint[AreaUnit] = Unconstrained[AreaUnit], + aspectRatio: Constraint[Rational] = Unconstrained[Rational] +) extends ConstrainedLayout { + + def level = 1 + + def mapElements(f: ((String, Int)) => String) = this.copy(mapElementsHelper(f), placements, width, height, area, aspectRatio) + +} + +private[floorplan] final case class ConstrainedLengthLayout( + elements: Seq[String], + placements: Seq[LengthPlacementConstraint], width: Constraint[LengthUnit] = Unconstrained[LengthUnit], height: Constraint[LengthUnit] = Unconstrained[LengthUnit], area: Constraint[AreaUnit] = Unconstrained[AreaUnit], diff --git a/floorplan/src/main/scala/Serialization.scala b/floorplan/src/main/scala/Serialization.scala index 0a715c08..a2daa449 100644 --- a/floorplan/src/main/scala/Serialization.scala +++ b/floorplan/src/main/scala/Serialization.scala @@ -11,7 +11,7 @@ object FloorplanSerialization { private val typeHintClasses = Seq( typeOf[Element], typeOf[Unit], - typeOf[RelativePlacementAnchor], + typeOf[PlacementAnchor], typeOf[Constraint[Rational]], typeOf[Constraint[LengthUnit]], typeOf[Constraint[AreaUnit]], diff --git a/floorplan/src/main/scala/chisel/API.scala b/floorplan/src/main/scala/chisel/API.scala index 31b27aed..2333d5bc 100644 --- a/floorplan/src/main/scala/chisel/API.scala +++ b/floorplan/src/main/scala/chisel/API.scala @@ -7,7 +7,7 @@ import firrtl.annotations.{ModuleTarget, InstanceTarget} import barstools.floorplan._ import barstools.floorplan.firrtl.{FloorplanAnnotation, FloorplanInstanceAnnotation, FloorplanModuleAnnotation, FloorplanGroupAnnotation} -import scala.collection.mutable.{ArraySeq, HashMap, Set, HashSet} +import scala.collection.mutable.{ArraySeq, ArrayBuffer, HashMap, Set, HashSet} final case class ChiselFloorplanException(message: String) extends Exception(message: String) @@ -25,6 +25,28 @@ object Floorplan { elt } + def createRatioLayout[T <: RawModule](module: T, + width: Constraint[LengthUnit] = Unconstrained[LengthUnit], + height: Constraint[LengthUnit] = Unconstrained[LengthUnit], + area: Constraint[AreaUnit] = Unconstrained[AreaUnit], + aspectRatio: Constraint[Rational] = Unconstrained[Rational] + ) = { + val elt = new ChiselConstrainedRatioLayout(module, width, height, area, aspectRatio) + FloorplanDatabase.register(module, elt) + elt + } + + def createLengthLayout[T <: RawModule](module: T, + width: Constraint[LengthUnit] = Unconstrained[LengthUnit], + height: Constraint[LengthUnit] = Unconstrained[LengthUnit], + area: Constraint[AreaUnit] = Unconstrained[AreaUnit], + aspectRatio: Constraint[Rational] = Unconstrained[Rational] + ) = { + val elt = new ChiselConstrainedLengthLayout(module, width, height, area, aspectRatio) + FloorplanDatabase.register(module, elt) + elt + } + def createPlaceholderRect[T <: RawModule](module: T, name: Option[String] = None, width: Constraint[LengthUnit] = Unconstrained[LengthUnit], @@ -229,6 +251,7 @@ final class ChiselWeightedGrid private[chisel] ( private var xDim = x private var yDim = y + // TODO change these data structures protected val elements = ArraySeq.fill[Option[ChiselElement]](xDim*yDim) { Option.empty[ChiselElement] } private val weights = ArraySeq.fill[Rational](xDim*yDim) { Rational(1) } @@ -257,8 +280,41 @@ final class ChiselWeightedGrid private[chisel] ( } +final class ChiselConstrainedRatioLayout private[chisel] ( + module: RawModule, + val width: Constraint[LengthUnit] = Unconstrained[LengthUnit], + val height: Constraint[LengthUnit] = Unconstrained[LengthUnit], + val area: Constraint[AreaUnit] = Unconstrained[AreaUnit], + val aspectRatio: Constraint[Rational] = Unconstrained[Rational] +) extends ChiselLayoutElement(module) { + + protected val elements = new ArrayBuffer[Option[ChiselElement]]() + protected val placements = new ArrayBuffer[RatioPlacementConstraint]() + + def add(element: ChiselElement, constraint: RatioPlacementConstraint): Unit = { + if (isCommitted) throw new ChiselFloorplanException("Cannot modify a ChiselConstrainedPlacement after committing") + elements.append(Some(element)) + placements.append(constraint) + } + + def add(element: ChiselElement, x: Constraint[Rational], y: Constraint[Rational], anchor: PlacementAnchor = LowerLeft()): Unit = add(element, RatioPlacementConstraint(x, y, anchor)) + + def addModule(elementModule: RawModule, constraint: RatioPlacementConstraint): Unit = add(Floorplan.createRect(elementModule), constraint) + def addModule(elementModule: RawModule, x: Constraint[Rational], y: Constraint[Rational], anchor: PlacementAnchor = LowerLeft()): Unit = addModule(elementModule, RatioPlacementConstraint(x, y, anchor)) + + protected def generateElement(): Element = { + ConstrainedRatioLayout( + elements.map(_.get.commit()), + placements, + width, + height, + area, + aspectRatio) + } -final class ChiselConstrainedRelativePlacement private[chisel] ( +} + +final class ChiselConstrainedLengthLayout private[chisel] ( module: RawModule, val width: Constraint[LengthUnit] = Unconstrained[LengthUnit], val height: Constraint[LengthUnit] = Unconstrained[LengthUnit], @@ -266,19 +322,22 @@ final class ChiselConstrainedRelativePlacement private[chisel] ( val aspectRatio: Constraint[Rational] = Unconstrained[Rational] ) extends ChiselLayoutElement(module) { - protected val elements = new ArraySeq[Option[ChiselElement]](0) - protected val placements = new ArraySeq[RelativePlacementConstraint](0) + protected val elements = new ArrayBuffer[Option[ChiselElement]]() + protected val placements = new ArrayBuffer[LengthPlacementConstraint]() - def add(element: ChiselElement, constraint: RelativePlacementConstraint): Unit = { - if (isCommitted) throw new ChiselFloorplanException("Cannot modify a ChiselConstrainedRelativePlacement after committing") - elements :+ Some(element) - placements :+ constraint + def add(element: ChiselElement, constraint: LengthPlacementConstraint): Unit = { + if (isCommitted) throw new ChiselFloorplanException("Cannot modify a ChiselConstrainedPlacement after committing") + elements.append(Some(element)) + placements.append(constraint) } - def addModule(elementModule: RawModule, constraint: RelativePlacementConstraint): Unit = add(Floorplan.createRect(elementModule), constraint) + def add(element: ChiselElement, x: Constraint[LengthUnit], y: Constraint[LengthUnit], anchor: PlacementAnchor = LowerLeft()): Unit = add(element, LengthPlacementConstraint(x, y, anchor)) + + def addModule(elementModule: RawModule, constraint: LengthPlacementConstraint): Unit = add(Floorplan.createRect(elementModule), constraint) + def addModule(elementModule: RawModule, x: Constraint[LengthUnit], y: Constraint[LengthUnit], anchor: PlacementAnchor = LowerLeft()): Unit = addModule(elementModule, LengthPlacementConstraint(x, y, anchor)) protected def generateElement(): Element = { - ConstrainedRelativePlacement( + ConstrainedLengthLayout( elements.map(_.get.commit()), placements, width, diff --git a/floorplan/src/main/scala/compiler/FloorplanCompiler.scala b/floorplan/src/main/scala/compiler/FloorplanCompiler.scala index 913e1d9f..84800b51 100644 --- a/floorplan/src/main/scala/compiler/FloorplanCompiler.scala +++ b/floorplan/src/main/scala/compiler/FloorplanCompiler.scala @@ -28,6 +28,4 @@ object FloorplanCompiler extends App { throw new Exception("Error parsing options!") } - - } diff --git a/floorplan/src/main/scala/compiler/FloorplanTree.scala b/floorplan/src/main/scala/compiler/FloorplanTree.scala index 804d6813..e931f8e9 100644 --- a/floorplan/src/main/scala/compiler/FloorplanTree.scala +++ b/floorplan/src/main/scala/compiler/FloorplanTree.scala @@ -2,7 +2,7 @@ package barstools.floorplan.compiler import scala.collection.mutable.{HashMap} -import barstools.floorplan.{Element, FloorplanState} +import barstools.floorplan.{Element, FloorplanState, FloorplanElementRecord} class FloorplanTree(state: FloorplanState) { @@ -42,6 +42,19 @@ class FloorplanTree(state: FloorplanState) { } def getElement(path: Seq[String], tag: String): Element = (if (path.isEmpty) this else getNode(path)).getElement(tag) + + def getElements: Seq[(String, Element)] = { + elements.toSeq ++ (children.mapValues(_.getElements).toSeq.map { case (name, elts) => + elts.map { case (path, elt) => + val newpath = if (name == "") path else s"${name}.${path}" + (newpath, elt) + } + }).flatten + } + + def mapElements(f: (Element) => Element): Seq[(String, Element)] = { + ??? + } } val root = new Node(None, "") @@ -71,6 +84,13 @@ class FloorplanTree(state: FloorplanState) { root.getElement(path, tag) } + def toState: FloorplanState = { + val records = root.getElements.map { case (path, elt) => FloorplanElementRecord(path, elt) } + FloorplanState(records, records.map(_.element.level).max) + } + + //def traverse( + } object FloorplanTree { diff --git a/floorplan/src/main/scala/compiler/Passes.scala b/floorplan/src/main/scala/compiler/Passes.scala index a0daf1f8..f4d4f6e4 100644 --- a/floorplan/src/main/scala/compiler/Passes.scala +++ b/floorplan/src/main/scala/compiler/Passes.scala @@ -25,6 +25,7 @@ class MacroLayoutsPass { class BottomUpPropagationPass { def execute(state: FloorplanState): FloorplanState = { + val tree = FloorplanTree(state) ??? } From f5738fde45edbcc4e0b668e657db057043d5f7c9 Mon Sep 17 00:00:00 2001 From: John Wright Date: Sun, 5 Apr 2020 18:44:00 -0700 Subject: [PATCH 18/73] Add preliminary README --- floorplan/README | 61 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 floorplan/README diff --git a/floorplan/README b/floorplan/README new file mode 100644 index 00000000..514bedef --- /dev/null +++ b/floorplan/README @@ -0,0 +1,61 @@ + +Placement/Boundary constraints + + +Schema: + +- `Element` is the base abstract class + +- `Primitive` extends `Element` (abstract) + - name (String) + - target (InstanceTarget) ?? (some way of tying this to an instance) + +- `Rect` extends `Primitive` (abstract) + - x (LengthUnit) + - y (LengthUnit) + - w (LengthUnit) + - h (LengthUnit) + - rot (Rotation) + +- `Macro` extends `Rect` + +- `RouteBlockage` extends `Rect` + - layer (String) + +- `PlaceBlockage` extends `Rect` + +- `SeqMem` extends `Rect` + +- `Group` extends `Element` (abstract) + +- `Grid` extends `Group` + - elements (Array2D[Element]) + +- `HorizontalArray` extends `Array` + - elements.height = 1 + +- `VerticalArray` extends `Array` + - elements.width = 1 + +- `LayoutOptions` extends `Element` (abstract) + - layouts (List[Group]) + +- `PriorityLayoutOptions` extends `LayoutOptions` + + + +IR Level 0 +- `Rect`s must contain x, y, rot + + +IR Level 1 +- `Rect`s must contain w, h + + +IR Level 2 +- `Rect`s must contain name, target, type + + + +- Need to have an "inline" way of saying "Replace the floorplan of this block with some external hammer IR" + From 94a1d82d254db8f7b3314623ce404c1af067730a Mon Sep 17 00:00:00 2001 From: John Wright Date: Tue, 12 May 2020 00:51:17 -0700 Subject: [PATCH 19/73] Start working on new Aspects stuff, WIP --- floorplan/src/main/scala/IR.scala | 11 ++--- floorplan/src/main/scala/chisel/API.scala | 49 ++++++++++++++----- .../main/scala/chisel/FloorplanAspect.scala | 20 ++++++++ .../src/main/scala/firrtl/Annotations.scala | 20 ++++++-- floorplan/src/main/scala/firrtl/Passes.scala | 14 +++--- 5 files changed, 83 insertions(+), 31 deletions(-) create mode 100644 floorplan/src/main/scala/chisel/FloorplanAspect.scala diff --git a/floorplan/src/main/scala/IR.scala b/floorplan/src/main/scala/IR.scala index ca7762a5..87cdc994 100644 --- a/floorplan/src/main/scala/IR.scala +++ b/floorplan/src/main/scala/IR.scala @@ -55,20 +55,15 @@ private[floorplan] final case class AbstractMacro(name: String) extends Abstract private[floorplan] final case class ConcreteMacro(name: String, width: LengthUnit, height: LengthUnit) extends ConcreteRectPrimitive with MacroLike -sealed trait IsModuleTop { - // Basically prevent subclasses from implementing name - final def name = "" -} - private[floorplan] final case class ConstrainedLogicRect( width: Constraint[LengthUnit], height: Constraint[LengthUnit], area: Constraint[AreaUnit], aspectRatio: Constraint[Rational], hardBoundary: Boolean -) extends ConstrainedRectPrimitive with IsModuleTop +) extends ConstrainedRectPrimitive -private[floorplan] final case class ConcreteLogicRect(width: LengthUnit, height: LengthUnit, hardBoundary: Boolean) extends ConcreteRectPrimitive with IsModuleTop +private[floorplan] final case class ConcreteLogicRect(width: LengthUnit, height: LengthUnit, hardBoundary: Boolean) extends ConcreteRectPrimitive sealed abstract class Group extends Element { @@ -106,7 +101,7 @@ private[floorplan] final case class WeightedGrid( } -sealed abstract class Layout extends Group with IsModuleTop +sealed abstract class Layout extends Group sealed abstract class ConstrainedLayout extends Layout with ConstrainedRectLike sealed abstract class ConcreteLayout extends Layout with ConcreteRectLike diff --git a/floorplan/src/main/scala/chisel/API.scala b/floorplan/src/main/scala/chisel/API.scala index 2333d5bc..e18bb940 100644 --- a/floorplan/src/main/scala/chisel/API.scala +++ b/floorplan/src/main/scala/chisel/API.scala @@ -3,10 +3,10 @@ package barstools.floorplan.chisel import chisel3.{RawModule} -import firrtl.annotations.{ModuleTarget, InstanceTarget} +import firrtl.annotations.{ModuleTarget, InstanceTarget, Annotation} import barstools.floorplan._ -import barstools.floorplan.firrtl.{FloorplanAnnotation, FloorplanInstanceAnnotation, FloorplanModuleAnnotation, FloorplanGroupAnnotation} +import barstools.floorplan.firrtl.{FloorplanAnnotation, FloorplanInstanceAnnotation, FloorplanModuleAnnotation, FloorplanGroupAnnotation, GenerateFloorplanIR} import scala.collection.mutable.{ArraySeq, ArrayBuffer, HashMap, Set, HashSet} final case class ChiselFloorplanException(message: String) extends Exception(message: String) @@ -21,10 +21,11 @@ object Floorplan { hardBoundary: Boolean = true ) = { val elt = new ChiselLogicRect(module, width, height, area, aspectRatio, hardBoundary) - FloorplanDatabase.register(module, elt) + //FloorplanDatabase.register(module, elt) elt } + /* def createRatioLayout[T <: RawModule](module: T, width: Constraint[LengthUnit] = Unconstrained[LengthUnit], height: Constraint[LengthUnit] = Unconstrained[LengthUnit], @@ -35,7 +36,9 @@ object Floorplan { FloorplanDatabase.register(module, elt) elt } + */ + /* def createLengthLayout[T <: RawModule](module: T, width: Constraint[LengthUnit] = Unconstrained[LengthUnit], height: Constraint[LengthUnit] = Unconstrained[LengthUnit], @@ -46,6 +49,7 @@ object Floorplan { FloorplanDatabase.register(module, elt) elt } + */ def createPlaceholderRect[T <: RawModule](module: T, name: Option[String] = None, @@ -54,12 +58,13 @@ object Floorplan { area: Constraint[AreaUnit] = Unconstrained[AreaUnit], aspectRatio: Constraint[Rational] = Unconstrained[Rational] ) = { - val nameStr = name.getOrElse(FloorplanDatabase.getUnusedName(module)) - val elt = new ChiselPlaceholderRect(module, nameStr, width, height, area, aspectRatio) - FloorplanDatabase.register(module, elt) - elt + //val nameStr = name.getOrElse(FloorplanDatabase.getUnusedName(module)) + val elt = new ChiselPlaceholderRect(module, name.getOrElse("TODO"), width, height, area, aspectRatio) + //FloorplanDatabase.register(module, elt) + Seq(elt) ++ GenerateFloorplanIR.emit() } + /* def createGrid[T <: RawModule](module: T, name: String, x: Int = 1, @@ -70,11 +75,13 @@ object Floorplan { FloorplanDatabase.register(module, elt) elt } + */ - def commitAndGetAnnotations(): Seq[FloorplanAnnotation] = FloorplanDatabase.commitAndGetAnnotations() + //def commitAndGetAnnotations(): Seq[FloorplanAnnotation] = FloorplanDatabase.commitAndGetAnnotations() } +/* private[chisel] object FloorplanDatabase { private val nameMap = new HashMap[RawModule, Set[String]]() @@ -105,6 +112,7 @@ private[chisel] object FloorplanDatabase { } } +*/ object FloorplanUnits { @@ -154,7 +162,7 @@ object FloorplanUnits { abstract class ChiselElement(val module: RawModule, val name: String) { - final private var committed = false + //final private var committed = false protected def generateElement(): Element @@ -162,10 +170,14 @@ abstract class ChiselElement(val module: RawModule, val name: String) { //final protected def targetName: (String, String) = (s"${module.toAbsoluteTarget.serialize}", name) // TODO FIXME this is clunky too - final protected var fpir: Element = null + //final protected var fpir: Element = null + final val fpir = generateElement() - def getAnnotation(): FloorplanAnnotation + protected def getFloorplanAnnotation(): FloorplanAnnotation + def getAnnotations(): Seq[Annotation] = GenerateFloorplanIR.emit() :+ getFloorplanAnnotation() + +/* def isCommitted = committed final private[chisel] def commit(): String = { @@ -175,13 +187,14 @@ abstract class ChiselElement(val module: RawModule, val name: String) { } name } +*/ } abstract class ChiselPrimitiveElement(module: RawModule, name: String) extends ChiselElement(module, name) { - def getAnnotation(): FloorplanAnnotation = { - if (!isCommitted) throw ChiselFloorplanException("Must commit ChiselElement before getting its annotation!") + def getFloorplanAnnotation() = { + //if (!isCommitted) throw ChiselFloorplanException("Must commit ChiselElement before getting its annotation!") module.toAbsoluteTarget match { case x: InstanceTarget => FloorplanInstanceAnnotation(x, fpir) case x: ModuleTarget => FloorplanModuleAnnotation(x, fpir) @@ -191,6 +204,7 @@ abstract class ChiselPrimitiveElement(module: RawModule, name: String) extends C } +/* abstract class ChiselGroupElement(module: RawModule, name: String) extends ChiselElement(module, name) { protected val elements: Seq[Option[ChiselElement]] @@ -207,8 +221,11 @@ abstract class ChiselGroupElement(module: RawModule, name: String) extends Chise } } +*/ +/* abstract class ChiselLayoutElement(module: RawModule) extends ChiselGroupElement(module, "") +*/ final class ChiselLogicRect private[chisel] ( module: RawModule, @@ -236,6 +253,7 @@ final class ChiselPlaceholderRect private[chisel] ( } +/* final class ChiselWeightedGrid private[chisel] ( module: RawModule, name: String, @@ -279,7 +297,9 @@ final class ChiselWeightedGrid private[chisel] ( } } +*/ +/* final class ChiselConstrainedRatioLayout private[chisel] ( module: RawModule, val width: Constraint[LengthUnit] = Unconstrained[LengthUnit], @@ -313,7 +333,9 @@ final class ChiselConstrainedRatioLayout private[chisel] ( } } +*/ +/* final class ChiselConstrainedLengthLayout private[chisel] ( module: RawModule, val width: Constraint[LengthUnit] = Unconstrained[LengthUnit], @@ -347,3 +369,4 @@ final class ChiselConstrainedLengthLayout private[chisel] ( } } +*/ diff --git a/floorplan/src/main/scala/chisel/FloorplanAspect.scala b/floorplan/src/main/scala/chisel/FloorplanAspect.scala new file mode 100644 index 00000000..5246a6b3 --- /dev/null +++ b/floorplan/src/main/scala/chisel/FloorplanAspect.scala @@ -0,0 +1,20 @@ +// See LICENSE for license details +package barstools.floorplan.chisel + +import barstools.floorplan.firrtl.{FloorplanAnnotation} + +import chisel3.{RawModule} +import chisel3.aop.{Aspect, Select} +import chisel3.experimental.{BaseModule} +import firrtl.{AnnotationSeq} + +abstract class FloorplanAspect extends Aspect[RawModule] { + + def floorplans: PartialFunction[BaseModule, Seq[ChiselElement]] + + final override def toAnnotation(top: RawModule): AnnotationSeq = { + // TODO we run this for each FloorplanAspect, which is inefficient. We should run Select.collectDeep only once and combine the partial functions somehow + AnnotationSeq(Select.collectDeep(top)(floorplans).toList.flatten.flatMap(_.getAnnotations())) + } +} + diff --git a/floorplan/src/main/scala/firrtl/Annotations.scala b/floorplan/src/main/scala/firrtl/Annotations.scala index 803d36f0..b49b2dd6 100644 --- a/floorplan/src/main/scala/firrtl/Annotations.scala +++ b/floorplan/src/main/scala/firrtl/Annotations.scala @@ -3,7 +3,8 @@ package barstools.floorplan.firrtl import barstools.floorplan.{Element, Group} -import firrtl.annotations.{Annotation, NoTargetAnnotation, SingleTargetAnnotation, MultiTargetAnnotation, Target, ModuleTarget, InstanceTarget, ReferenceTarget, IsModule} +import firrtl.annotations._ +import firrtl.stage.{RunFirrtlTransformAnnotation} // John: Right now we are using ModuleTarget, which will mean that all instances of the same master // will have the same floorplan. This is probably OK for now, but eventually we will want to support @@ -17,15 +18,15 @@ trait FloorplanAnnotation extends Annotation { } case class FloorplanModuleAnnotation(target: ModuleTarget, fpir: String) extends SingleTargetAnnotation[ModuleTarget] with FloorplanAnnotation { - def duplicate(t: ModuleTarget) = this.copy(target, fpir) + def duplicate(t: ModuleTarget) = this.copy(t, fpir) } case class FloorplanInstanceAnnotation(target: InstanceTarget, fpir: String) extends SingleTargetAnnotation[InstanceTarget] with FloorplanAnnotation { - def duplicate(t: InstanceTarget) = this.copy(target, fpir) + def duplicate(t: InstanceTarget) = this.copy(t, fpir) } case class FloorplanGroupAnnotation(targets: Seq[Seq[Target]], fpir: String) extends MultiTargetAnnotation with FloorplanAnnotation { - def duplicate(t: Seq[Seq[Target]]) = this.copy(targets, fpir) + def duplicate(t: Seq[Seq[Target]]) = this.copy(t, fpir) } object FloorplanModuleAnnotation { @@ -43,3 +44,14 @@ object FloorplanGroupAnnotation { } case class FloorplanIRFileAnnotation(value: String) extends NoTargetAnnotation + +object GenerateFloorplanIR { + private var emitted = false + + def emit(): Seq[RunFirrtlTransformAnnotation] = { + if (emitted) Nil else { + emitted = true + Seq(RunFirrtlTransformAnnotation(new GenerateFloorplanIRPass)) + } + } +} diff --git a/floorplan/src/main/scala/firrtl/Passes.scala b/floorplan/src/main/scala/firrtl/Passes.scala index f17087e7..2931c5ee 100644 --- a/floorplan/src/main/scala/firrtl/Passes.scala +++ b/floorplan/src/main/scala/firrtl/Passes.scala @@ -2,16 +2,19 @@ package barstools.floorplan.firrtl import barstools.floorplan.{FloorplanSerialization, FloorplanElementRecord, FloorplanState} -import firrtl.{CircuitState, LowForm, Namespace, Transform, AnnotationSeq} -import firrtl.options.{RegisteredTransform, ShellOption} +import firrtl.{CircuitState, Namespace, Transform, AnnotationSeq, VerilogEmitter, DependencyAPIMigration} +import firrtl.options.{Dependency, RegisteredTransform, ShellOption} import firrtl.analyses.{InstanceGraph} import firrtl.annotations.{InstanceTarget, ModuleTarget} // NOTE: If you rename/add this transform, don't forget to update META-INF // See the @note in the RegisteredTransform documentation -class GenerateFloorplanIRPass extends Transform with RegisteredTransform { - def inputForm = LowForm - def outputForm = LowForm +class GenerateFloorplanIRPass extends Transform with RegisteredTransform with DependencyAPIMigration { + + override def prerequisites = Seq(Dependency[VerilogEmitter]) + override def optionalPrerequisites = Nil + override def dependents = Nil + override def invalidates(xform: Transform) = false val options = Seq( new ShellOption[String]( @@ -21,7 +24,6 @@ class GenerateFloorplanIRPass extends Transform with RegisteredTransform { ) ) - private def getInstancePathsFromGraph(graph: InstanceGraph, cktName: String, name: String): Seq[String] = { if (cktName == name) { return Seq("") From 612c9623ea06c14f0c395d64af07dcc826cb9803 Mon Sep 17 00:00:00 2001 From: John Wright Date: Wed, 13 May 2020 22:03:29 -0700 Subject: [PATCH 20/73] Some API tweaks --- floorplan/src/main/scala/IR.scala | 34 ++++-- floorplan/src/main/scala/chisel/API.scala | 114 +++++++----------- .../main/scala/chisel/FloorplanAspect.scala | 7 +- .../src/main/scala/firrtl/Annotations.scala | 2 +- floorplan/src/main/scala/firrtl/Passes.scala | 2 +- 5 files changed, 68 insertions(+), 91 deletions(-) diff --git a/floorplan/src/main/scala/IR.scala b/floorplan/src/main/scala/IR.scala index 87cdc994..259a163b 100644 --- a/floorplan/src/main/scala/IR.scala +++ b/floorplan/src/main/scala/IR.scala @@ -7,6 +7,7 @@ package barstools.floorplan sealed abstract class Element { + def name: String // TODO make this an Enumeration def level: Int def serialize = FloorplanSerialization.serialize(this) @@ -18,15 +19,15 @@ sealed abstract class Primitive extends Element ////////////////////////////////////////////// Rect shape trait ConstrainedRectLike { - val width: Constraint[LengthUnit] - val height: Constraint[LengthUnit] - val area: Constraint[AreaUnit] - val aspectRatio: Constraint[Rational] + def width: Constraint[LengthUnit] + def height: Constraint[LengthUnit] + def area: Constraint[AreaUnit] + def aspectRatio: Constraint[Rational] } trait ConcreteRectLike { - val width: LengthUnit - val height: LengthUnit + def width: LengthUnit + def height: LengthUnit } sealed abstract class AbstractRectPrimitive extends Primitive { @@ -41,7 +42,7 @@ sealed abstract class ConcreteRectPrimitive extends Primitive with ConcreteRectL final def level = 0 } -private[floorplan] final case class ConstrainedPlaceholderRect( +private[floorplan] final case class ConstrainedDummyRect( name: String, width: Constraint[LengthUnit] = Unconstrained[LengthUnit], height: Constraint[LengthUnit] = Unconstrained[LengthUnit], @@ -61,13 +62,17 @@ private[floorplan] final case class ConstrainedLogicRect( area: Constraint[AreaUnit], aspectRatio: Constraint[Rational], hardBoundary: Boolean -) extends ConstrainedRectPrimitive +) extends ConstrainedRectPrimitive { + def name = "" +} -private[floorplan] final case class ConcreteLogicRect(width: LengthUnit, height: LengthUnit, hardBoundary: Boolean) extends ConcreteRectPrimitive +private[floorplan] final case class ConcreteLogicRect(width: LengthUnit, height: LengthUnit, hardBoundary: Boolean) extends ConcreteRectPrimitive { + def name = "" +} sealed abstract class Group extends Element { - val elements: Seq[String] + def elements: Seq[String] def mapElements(f: ((String, Int)) => String): Group @@ -76,9 +81,9 @@ sealed abstract class Group extends Element { } sealed abstract class Grid extends Group { - val xDim: Int - val yDim: Int - val elements: Seq[String] + def xDim: Int + def yDim: Int + def elements: Seq[String] assert(xDim > 0, "X dimension of grid must be positive") assert(yDim > 0, "Y dimension of grid must be positive") @@ -101,6 +106,7 @@ private[floorplan] final case class WeightedGrid( } +/* sealed abstract class Layout extends Group sealed abstract class ConstrainedLayout extends Layout with ConstrainedRectLike sealed abstract class ConcreteLayout extends Layout with ConcreteRectLike @@ -135,4 +141,6 @@ private[floorplan] final case class ConstrainedLengthLayout( } +*/ + // TODO add more layouts here diff --git a/floorplan/src/main/scala/chisel/API.scala b/floorplan/src/main/scala/chisel/API.scala index e18bb940..7b7e6aac 100644 --- a/floorplan/src/main/scala/chisel/API.scala +++ b/floorplan/src/main/scala/chisel/API.scala @@ -13,7 +13,7 @@ final case class ChiselFloorplanException(message: String) extends Exception(mes object Floorplan { - def createRect[T <: RawModule](module: T, + def rect[T <: RawModule](module: T, width: Constraint[LengthUnit] = Unconstrained[LengthUnit], height: Constraint[LengthUnit] = Unconstrained[LengthUnit], area: Constraint[AreaUnit] = Unconstrained[AreaUnit], @@ -21,7 +21,7 @@ object Floorplan { hardBoundary: Boolean = true ) = { val elt = new ChiselLogicRect(module, width, height, area, aspectRatio, hardBoundary) - //FloorplanDatabase.register(module, elt) + FloorplanDatabase.register(module, elt) elt } @@ -51,21 +51,20 @@ object Floorplan { } */ - def createPlaceholderRect[T <: RawModule](module: T, + def dummy[T <: RawModule](module: T, name: Option[String] = None, width: Constraint[LengthUnit] = Unconstrained[LengthUnit], height: Constraint[LengthUnit] = Unconstrained[LengthUnit], area: Constraint[AreaUnit] = Unconstrained[AreaUnit], aspectRatio: Constraint[Rational] = Unconstrained[Rational] ) = { - //val nameStr = name.getOrElse(FloorplanDatabase.getUnusedName(module)) - val elt = new ChiselPlaceholderRect(module, name.getOrElse("TODO"), width, height, area, aspectRatio) - //FloorplanDatabase.register(module, elt) - Seq(elt) ++ GenerateFloorplanIR.emit() + val nameStr = name.getOrElse(FloorplanDatabase.getUnusedName(module)) + val elt = new ChiselDummyRect(module, nameStr, width, height, area, aspectRatio) + FloorplanDatabase.register(module, elt) + elt } - /* - def createGrid[T <: RawModule](module: T, + def grid[T <: RawModule](module: T, name: String, x: Int = 1, y: Int = 1, @@ -75,13 +74,9 @@ object Floorplan { FloorplanDatabase.register(module, elt) elt } - */ - - //def commitAndGetAnnotations(): Seq[FloorplanAnnotation] = FloorplanDatabase.commitAndGetAnnotations() } -/* private[chisel] object FloorplanDatabase { private val nameMap = new HashMap[RawModule, Set[String]]() @@ -106,13 +101,7 @@ private[chisel] object FloorplanDatabase { suggestion + s"_${id}" } - def commitAndGetAnnotations(): Seq[FloorplanAnnotation] = { - elements.foreach(_.commit()) - elements.toSeq.map(_.getAnnotation()) - } - } -*/ object FloorplanUnits { @@ -162,66 +151,46 @@ object FloorplanUnits { abstract class ChiselElement(val module: RawModule, val name: String) { - //final private var committed = false - protected def generateElement(): Element - // TODO FIXME this is clunky - //final protected def targetName: (String, String) = (s"${module.toAbsoluteTarget.serialize}", name) - - // TODO FIXME this is clunky too - //final protected var fpir: Element = null - final val fpir = generateElement() + private[chisel] def getFloorplanAnnotations(): Seq[FloorplanAnnotation] - protected def getFloorplanAnnotation(): FloorplanAnnotation - - def getAnnotations(): Seq[Annotation] = GenerateFloorplanIR.emit() :+ getFloorplanAnnotation() - -/* - def isCommitted = committed - - final private[chisel] def commit(): String = { - if (!isCommitted) { - committed = true - fpir = generateElement() - } - name - } -*/ + def getAnnotations(): Seq[Annotation] = GenerateFloorplanIR.emit() ++ getFloorplanAnnotations() } abstract class ChiselPrimitiveElement(module: RawModule, name: String) extends ChiselElement(module, name) { - def getFloorplanAnnotation() = { - //if (!isCommitted) throw ChiselFloorplanException("Must commit ChiselElement before getting its annotation!") + private[chisel] def getFloorplanAnnotations() = { module.toAbsoluteTarget match { - case x: InstanceTarget => FloorplanInstanceAnnotation(x, fpir) - case x: ModuleTarget => FloorplanModuleAnnotation(x, fpir) + case x: InstanceTarget => Seq(FloorplanInstanceAnnotation(x, generateElement())) + case x: ModuleTarget => Seq(FloorplanModuleAnnotation(x, generateElement())) case _ => ??? } } } -/* + abstract class ChiselGroupElement(module: RawModule, name: String) extends ChiselElement(module, name) { - protected val elements: Seq[Option[ChiselElement]] + protected def elements: Seq[Option[ChiselElement]] + + def isCommitted: Boolean - def getAnnotation(): FloorplanAnnotation = { - if (!isCommitted) throw ChiselFloorplanException("Must commit ChiselElement before getting its annotation!") + private[chisel] def getFloorplanAnnotations(): Seq[FloorplanAnnotation] = { // The head of elements will be this module, while the remaining ones are the children + val fpir = generateElement().asInstanceOf[Group] val elementTargets = this.module.toAbsoluteTarget +: elements.map(_.get.module.toAbsoluteTarget) val elementTargetsMapped: Seq[Seq[InstanceTarget]] = elementTargets.map { x => assert(x.isInstanceOf[InstanceTarget], "All targets must be InstanceTargets for ChiselGroupElement annotations") Seq(x.asInstanceOf[InstanceTarget]) } - FloorplanGroupAnnotation(elementTargetsMapped, fpir.asInstanceOf[Group]) + elements.foreach { x => assert(x.isDefined, "Elements must be defined before running getFloorplanAnnotations()") } + elements.flatMap(_.get.getFloorplanAnnotations()) :+ FloorplanGroupAnnotation(elementTargetsMapped, fpir) } } -*/ /* abstract class ChiselLayoutElement(module: RawModule) extends ChiselGroupElement(module, "") @@ -240,7 +209,7 @@ final class ChiselLogicRect private[chisel] ( } -final class ChiselPlaceholderRect private[chisel] ( +final class ChiselDummyRect private[chisel] ( module: RawModule, name: String, val width: Constraint[LengthUnit] = Unconstrained[LengthUnit], @@ -249,55 +218,60 @@ final class ChiselPlaceholderRect private[chisel] ( val aspectRatio: Constraint[Rational] = Unconstrained[Rational] ) extends ChiselPrimitiveElement(module, name) { - protected def generateElement(): Element = ConstrainedPlaceholderRect(name, width, height, area, aspectRatio) + protected def generateElement(): Element = ConstrainedDummyRect(name, width, height, area, aspectRatio) } -/* final class ChiselWeightedGrid private[chisel] ( module: RawModule, name: String, - val x: Int, - val y: Int, + val xDim: Int, + val yDim: Int, val packed: Boolean ) extends ChiselGroupElement(module, name) { - assert(x > 0) - assert(y > 0) + assert(xDim > 0) + assert(yDim > 0) - // TODO add resizing APIs - private var xDim = x - private var yDim = y - - // TODO change these data structures protected val elements = ArraySeq.fill[Option[ChiselElement]](xDim*yDim) { Option.empty[ChiselElement] } private val weights = ArraySeq.fill[Rational](xDim*yDim) { Rational(1) } - def set(x: Int, y: Int, element: ChiselElement, weight: Rational = Rational(1)): Unit = { + private var _isCommitted = false + + def isCommitted = _isCommitted + + def set(x: Int, y: Int, element: ChiselElement, weight: Rational): Unit = { if (isCommitted) throw new ChiselFloorplanException("Cannot modify a ChiselWeightedGrid after committing") if (x >= xDim) throw new IndexOutOfBoundsException(s"X-value ${x} >= ${xDim} in ChiselWeightedGrid") if (y >= yDim) throw new IndexOutOfBoundsException(s"Y-value ${y} >= ${yDim} in ChiselWeightedGrid") + if (elements(y*xDim + x).isDefined) throw new ChiselFloorplanException(s"Coordinates (${x},${y}) already in use") elements(y*xDim + x) = Some(element) weights(y*xDim + x) = weight } - def setModule(x: Int, y: Int, elementModule: RawModule, weight: Rational = Rational(1)): Unit = set(x, y, Floorplan.createRect(elementModule), weight) + def set(x: Int, y: Int, element: ChiselElement): Unit = set(x, y, element, Rational(1)) - private def convertNonesToPlaceholders() = elements.transform(_.orElse(Some(Floorplan.createPlaceholderRect(module)))) + def set(x: Int, y: Int, eltModule: RawModule, weight: Rational): Unit = { + // TODO what should the hardness of this boundary be? + val element = new ChiselLogicRect(eltModule, Unconstrained[LengthUnit], Unconstrained[LengthUnit], Unconstrained[AreaUnit], Unconstrained[Rational], true) + set(x, y, element, weight) + } + + def set(x: Int, y: Int, module: RawModule): Unit = set(x, y, module, Rational(1)) protected def generateElement(): Element = { - convertNonesToPlaceholders() + _isCommitted = true + elements.transform(_.orElse(Some(Floorplan.dummy(module)))) WeightedGrid( name, xDim, yDim, - elements.map(_.get.commit()), + elements.map(_.get.name), weights, packed) } } -*/ /* final class ChiselConstrainedRatioLayout private[chisel] ( diff --git a/floorplan/src/main/scala/chisel/FloorplanAspect.scala b/floorplan/src/main/scala/chisel/FloorplanAspect.scala index 5246a6b3..be73de25 100644 --- a/floorplan/src/main/scala/chisel/FloorplanAspect.scala +++ b/floorplan/src/main/scala/chisel/FloorplanAspect.scala @@ -1,17 +1,12 @@ // See LICENSE for license details package barstools.floorplan.chisel -import barstools.floorplan.firrtl.{FloorplanAnnotation} - import chisel3.{RawModule} import chisel3.aop.{Aspect, Select} import chisel3.experimental.{BaseModule} import firrtl.{AnnotationSeq} -abstract class FloorplanAspect extends Aspect[RawModule] { - - def floorplans: PartialFunction[BaseModule, Seq[ChiselElement]] - +abstract class FloorplanAspect(floorplans: PartialFunction[BaseModule, Seq[ChiselElement]]) extends Aspect[RawModule] { final override def toAnnotation(top: RawModule): AnnotationSeq = { // TODO we run this for each FloorplanAspect, which is inefficient. We should run Select.collectDeep only once and combine the partial functions somehow AnnotationSeq(Select.collectDeep(top)(floorplans).toList.flatten.flatMap(_.getAnnotations())) diff --git a/floorplan/src/main/scala/firrtl/Annotations.scala b/floorplan/src/main/scala/firrtl/Annotations.scala index b49b2dd6..a913a962 100644 --- a/floorplan/src/main/scala/firrtl/Annotations.scala +++ b/floorplan/src/main/scala/firrtl/Annotations.scala @@ -40,7 +40,7 @@ object FloorplanInstanceAnnotation { object FloorplanGroupAnnotation { //def apply(targets: Seq[Target], fpir: String): FloorplanGroupAnnotation = FloorplanGroupAnnotation(targets.map(Seq(_)), fpir) //def apply(targets: Seq[Target], element: Element): FloorplanGroupAnnotation = FloorplanGroupAnnotation(targets, element.serialize) - def apply(targets: Seq[Seq[Target]], element: Group): FloorplanGroupAnnotation = FloorplanGroupAnnotation(targets, element.serialize) + def apply(targets: Seq[Seq[InstanceTarget]], element: Group): FloorplanGroupAnnotation = FloorplanGroupAnnotation(targets, element.serialize) } case class FloorplanIRFileAnnotation(value: String) extends NoTargetAnnotation diff --git a/floorplan/src/main/scala/firrtl/Passes.scala b/floorplan/src/main/scala/firrtl/Passes.scala index 2931c5ee..b02b8a9f 100644 --- a/floorplan/src/main/scala/firrtl/Passes.scala +++ b/floorplan/src/main/scala/firrtl/Passes.scala @@ -55,7 +55,7 @@ class GenerateFloorplanIRPass extends Transform with RegisteredTransform with De val element = FloorplanSerialization. deserialize(x.fpir). asInstanceOf[barstools.floorplan.Group]. - mapElements { case (name, id) => paths(id + 1) + "#" + name } + mapElements { case (name, id) => paths(id + 1) + (if (name == "") "" else "#" + name) } Seq(FloorplanElementRecord(paths(0), element)) } }).flatten From a1131ff590d395c5222f65331bb189a7feaa61aa Mon Sep 17 00:00:00 2001 From: John Wright Date: Sun, 11 Jul 2021 00:15:07 -0700 Subject: [PATCH 21/73] Update build.sbt --- build.sbt | 41 ++++++++++++++++++----------------------- 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/build.sbt b/build.sbt index b6b50ed8..63ea3ac2 100644 --- a/build.sbt +++ b/build.sbt @@ -5,29 +5,24 @@ val defaultVersions = Map( "chisel-iotesters" -> "2.5.1" ) -organization := "edu.berkeley.cs" -version := "0.4-SNAPSHOT" -name := "tapeout" -scalaVersion := "2.12.13" -crossScalaVersions := Seq("2.12.13", "2.13.6") -scalacOptions := Seq("-deprecation", "-feature", "-language:reflectiveCalls") -Test / scalacOptions ++= Seq("-language:reflectiveCalls") -fork := true -mainClass := Some("barstools.macros.MacroCompiler") -libraryDependencies ++= Seq("chisel3","chisel-iotesters").map { - dep: String => "edu.berkeley.cs" %% dep % sys.props.getOrElse(dep + "Version", defaultVersions(dep)) -} -libraryDependencies ++= Seq( - "com.typesafe.play" %% "play-json" % "2.9.2", - "org.scalatest" %% "scalatest" % "3.2.9" % "test", - "org.apache.logging.log4j" % "log4j-api" % "2.11.2", - "org.apache.logging.log4j" % "log4j-core" % "2.11.2" -) -addCompilerPlugin("edu.berkeley.cs" % "chisel3-plugin" % defaultVersions("chisel3") cross CrossVersion.full) -resolvers ++= Seq( - Resolver.sonatypeRepo("snapshots"), - Resolver.sonatypeRepo("releases"), - Resolver.mavenLocal +lazy val commonSettings = Seq( + organization := "edu.berkeley.cs", + version := "0.4-SNAPSHOT", + scalaVersion := "2.12.10", + scalacOptions := Seq("-deprecation", "-feature", "-language:reflectiveCalls", "-Xsource:2.11"), + libraryDependencies ++= Seq("chisel3","chisel-iotesters").map { + dep: String => "edu.berkeley.cs" %% dep % sys.props.getOrElse(dep + "Version", defaultVersions(dep)) + }, + libraryDependencies ++= Seq( + "org.scalatest" %% "scalatest" % "3.2.2" % "test", + "org.json4s" %% "json4s-jackson" % "3.6.1", + "org.json4s" %% "json4s-native" % "3.6.1", + ), + resolvers ++= Seq( + Resolver.sonatypeRepo("snapshots"), + Resolver.sonatypeRepo("releases"), + Resolver.mavenLocal + ) ) disablePlugins(sbtassembly.AssemblyPlugin) From f658e0dd04dc52200a5636c1cf5fe3f4c238c4e5 Mon Sep 17 00:00:00 2001 From: John Wright Date: Sun, 11 Jul 2021 15:13:31 -0700 Subject: [PATCH 22/73] Make something that actually produces floorplan IR --- floorplan/src/main/scala/FloorplanState.scala | 2 +- floorplan/src/main/scala/IR.scala | 6 +++ floorplan/src/main/scala/chisel/API.scala | 9 +++-- .../main/scala/chisel/FloorplanAspect.scala | 20 ++++++++-- floorplan/src/main/scala/chisel/package.scala | 11 ++++++ .../src/main/scala/firrtl/Annotations.scala | 10 ----- floorplan/src/main/scala/firrtl/Passes.scala | 39 +++++++++++-------- .../transforms/GenerateTopAndHarness.scala | 4 ++ 8 files changed, 65 insertions(+), 36 deletions(-) create mode 100644 floorplan/src/main/scala/chisel/package.scala diff --git a/floorplan/src/main/scala/FloorplanState.scala b/floorplan/src/main/scala/FloorplanState.scala index a032185f..fca49dda 100644 --- a/floorplan/src/main/scala/FloorplanState.scala +++ b/floorplan/src/main/scala/FloorplanState.scala @@ -7,7 +7,7 @@ final case class FloorplanState(elements: Seq[FloorplanElementRecord], level: In object FloorplanState { - def fromSeq(seq: Seq[FloorplanElementRecord]): FloorplanState = FloorplanState(seq, seq.map(_.element.level).max) + def fromSeq(seq: Seq[FloorplanElementRecord]): FloorplanState = FloorplanState(seq, (Seq(IRLevel.max) ++ seq.map(_.element.level)).max) def serialize(state: FloorplanState): String = FloorplanSerialization.serializeState(state) def serialize(seq: Seq[FloorplanElementRecord]): String = serialize(fromSeq(seq)) diff --git a/floorplan/src/main/scala/IR.scala b/floorplan/src/main/scala/IR.scala index 259a163b..8dfe3240 100644 --- a/floorplan/src/main/scala/IR.scala +++ b/floorplan/src/main/scala/IR.scala @@ -30,6 +30,12 @@ trait ConcreteRectLike { def height: LengthUnit } +object IRLevel { + + def max = 2 + +} + sealed abstract class AbstractRectPrimitive extends Primitive { final def level = 2 } diff --git a/floorplan/src/main/scala/chisel/API.scala b/floorplan/src/main/scala/chisel/API.scala index 7b7e6aac..985c9a89 100644 --- a/floorplan/src/main/scala/chisel/API.scala +++ b/floorplan/src/main/scala/chisel/API.scala @@ -6,7 +6,7 @@ import chisel3.{RawModule} import firrtl.annotations.{ModuleTarget, InstanceTarget, Annotation} import barstools.floorplan._ -import barstools.floorplan.firrtl.{FloorplanAnnotation, FloorplanInstanceAnnotation, FloorplanModuleAnnotation, FloorplanGroupAnnotation, GenerateFloorplanIR} +import barstools.floorplan.firrtl.{FloorplanAnnotation, FloorplanInstanceAnnotation, FloorplanModuleAnnotation, FloorplanGroupAnnotation} import scala.collection.mutable.{ArraySeq, ArrayBuffer, HashMap, Set, HashSet} final case class ChiselFloorplanException(message: String) extends Exception(message: String) @@ -153,16 +153,17 @@ abstract class ChiselElement(val module: RawModule, val name: String) { protected def generateElement(): Element + // TODO if there are no other annotations, this separation may no longer be needed private[chisel] def getFloorplanAnnotations(): Seq[FloorplanAnnotation] - def getAnnotations(): Seq[Annotation] = GenerateFloorplanIR.emit() ++ getFloorplanAnnotations() + def getAnnotations(): Seq[Annotation] = getFloorplanAnnotations() } abstract class ChiselPrimitiveElement(module: RawModule, name: String) extends ChiselElement(module, name) { private[chisel] def getFloorplanAnnotations() = { - module.toAbsoluteTarget match { + module.toTarget match { case x: InstanceTarget => Seq(FloorplanInstanceAnnotation(x, generateElement())) case x: ModuleTarget => Seq(FloorplanModuleAnnotation(x, generateElement())) case _ => ??? @@ -181,7 +182,7 @@ abstract class ChiselGroupElement(module: RawModule, name: String) extends Chise private[chisel] def getFloorplanAnnotations(): Seq[FloorplanAnnotation] = { // The head of elements will be this module, while the remaining ones are the children val fpir = generateElement().asInstanceOf[Group] - val elementTargets = this.module.toAbsoluteTarget +: elements.map(_.get.module.toAbsoluteTarget) + val elementTargets = this.module.toTarget +: elements.map(_.get.module.toTarget) val elementTargetsMapped: Seq[Seq[InstanceTarget]] = elementTargets.map { x => assert(x.isInstanceOf[InstanceTarget], "All targets must be InstanceTargets for ChiselGroupElement annotations") Seq(x.asInstanceOf[InstanceTarget]) diff --git a/floorplan/src/main/scala/chisel/FloorplanAspect.scala b/floorplan/src/main/scala/chisel/FloorplanAspect.scala index be73de25..60952d4a 100644 --- a/floorplan/src/main/scala/chisel/FloorplanAspect.scala +++ b/floorplan/src/main/scala/chisel/FloorplanAspect.scala @@ -3,13 +3,25 @@ package barstools.floorplan.chisel import chisel3.{RawModule} import chisel3.aop.{Aspect, Select} -import chisel3.experimental.{BaseModule} import firrtl.{AnnotationSeq} -abstract class FloorplanAspect(floorplans: PartialFunction[BaseModule, Seq[ChiselElement]]) extends Aspect[RawModule] { - final override def toAnnotation(top: RawModule): AnnotationSeq = { - // TODO we run this for each FloorplanAspect, which is inefficient. We should run Select.collectDeep only once and combine the partial functions somehow +abstract class FloorplanAspect[T <: RawModule](floorplans: FloorplanFunction) extends Aspect[T] { + + // Guarantee only one FloorplanAspect will be created + FloorplanAspect.setHasRun() + + final override def toAnnotation(top: T): AnnotationSeq = { AnnotationSeq(Select.collectDeep(top)(floorplans).toList.flatten.flatMap(_.getAnnotations())) } } +object FloorplanAspect { + private var hasRun = false + + private[chisel] def setHasRun() = { + if (hasRun) { + throw new Exception("Cannot run FloorplanAspect more than once; combine partial functions into a single FloorplanAspect instead.") + } + hasRun = true + } +} diff --git a/floorplan/src/main/scala/chisel/package.scala b/floorplan/src/main/scala/chisel/package.scala new file mode 100644 index 00000000..6415cc0c --- /dev/null +++ b/floorplan/src/main/scala/chisel/package.scala @@ -0,0 +1,11 @@ +// See LICENSE for license details +package barstools.floorplan + +package object chisel { + + import chisel3.experimental.{BaseModule} + + type FloorplanFunction = PartialFunction[BaseModule, Seq[ChiselElement]] + +} + diff --git a/floorplan/src/main/scala/firrtl/Annotations.scala b/floorplan/src/main/scala/firrtl/Annotations.scala index a913a962..253eaa0c 100644 --- a/floorplan/src/main/scala/firrtl/Annotations.scala +++ b/floorplan/src/main/scala/firrtl/Annotations.scala @@ -45,13 +45,3 @@ object FloorplanGroupAnnotation { case class FloorplanIRFileAnnotation(value: String) extends NoTargetAnnotation -object GenerateFloorplanIR { - private var emitted = false - - def emit(): Seq[RunFirrtlTransformAnnotation] = { - if (emitted) Nil else { - emitted = true - Seq(RunFirrtlTransformAnnotation(new GenerateFloorplanIRPass)) - } - } -} diff --git a/floorplan/src/main/scala/firrtl/Passes.scala b/floorplan/src/main/scala/firrtl/Passes.scala index b02b8a9f..622e2277 100644 --- a/floorplan/src/main/scala/firrtl/Passes.scala +++ b/floorplan/src/main/scala/firrtl/Passes.scala @@ -4,7 +4,7 @@ package barstools.floorplan.firrtl import barstools.floorplan.{FloorplanSerialization, FloorplanElementRecord, FloorplanState} import firrtl.{CircuitState, Namespace, Transform, AnnotationSeq, VerilogEmitter, DependencyAPIMigration} import firrtl.options.{Dependency, RegisteredTransform, ShellOption} -import firrtl.analyses.{InstanceGraph} +import firrtl.analyses.{InstanceKeyGraph} import firrtl.annotations.{InstanceTarget, ModuleTarget} // NOTE: If you rename/add this transform, don't forget to update META-INF @@ -24,18 +24,18 @@ class GenerateFloorplanIRPass extends Transform with RegisteredTransform with De ) ) - private def getInstancePathsFromGraph(graph: InstanceGraph, cktName: String, name: String): Seq[String] = { + private def getInstancePathsFromGraph(graph: InstanceKeyGraph, cktName: String, name: String): Seq[String] = { if (cktName == name) { - return Seq("") + Seq("") } else { - return graph.findInstancesInHierarchy(name).map(_.map(_.name).mkString(".") + ".") + graph.findInstancesInHierarchy(name).map(_.map(_.name).mkString(".") + ".") } } def execute(state: CircuitState): CircuitState = { // TODO don't need graph if there are no annos, which can be a speedup - val graph = new InstanceGraph(state.circuit) + val graph = InstanceKeyGraph(state.circuit) def getPaths(name: String): Seq[String] = getInstancePathsFromGraph(graph, state.circuit.main, name) def getInstancePath(t: InstanceTarget): String = { @@ -47,9 +47,14 @@ class GenerateFloorplanIRPass extends Transform with RegisteredTransform with De def newRecord(path: String, anno: FloorplanAnnotation) = FloorplanElementRecord(path, FloorplanSerialization.deserialize(anno.fpir)) val list = state.annotations.collect({ - case x: FloorplanInstanceAnnotation => Seq(newRecord(getInstancePath(x.target), x)) - case x: FloorplanModuleAnnotation => getPaths(x.target.name).map(newRecord(_, x)) + case x: FloorplanInstanceAnnotation => + throw new Exception("Don't use this yet") + //Seq(newRecord(getInstancePath(x.target), x)) + case x: FloorplanModuleAnnotation => + getPaths(x.target.name).map(newRecord(_, x)) case x: FloorplanGroupAnnotation => { + throw new Exception("Don't use this yet") + /* val paths = x.targets.map(_(0).asInstanceOf[InstanceTarget]).map(getInstancePath) // paths(0) is special; it's the path to the module the element is attached to val element = FloorplanSerialization. @@ -57,20 +62,20 @@ class GenerateFloorplanIRPass extends Transform with RegisteredTransform with De asInstanceOf[barstools.floorplan.Group]. mapElements { case (name, id) => paths(id + 1) + (if (name == "") "" else "#" + name) } Seq(FloorplanElementRecord(paths(0), element)) + */ } }).flatten - if (list.nonEmpty) { - val filename = state.annotations.collectFirst({ - case x: FloorplanIRFileAnnotation => x.value - }).getOrElse { - val opt = options.head.longOption - throw new Exception(s"Did not specify a filename for GenerateFloorplanIRPass. Please provide a FloorplanIRFileAnnotation or use the --${opt} option.") - } - val writer = new java.io.FileWriter(filename) - writer.write(FloorplanState.serialize(list)) - writer.close() + val filename = state.annotations.collectFirst({ + case x: FloorplanIRFileAnnotation => x.value + }).getOrElse { + val opt = options.head.longOption + throw new Exception(s"Did not specify a filename for GenerateFloorplanIRPass. Please provide a FloorplanIRFileAnnotation or use the --${opt} option.") } + val writer = new java.io.FileWriter(filename) + writer.write(FloorplanState.serialize(list)) + writer.close() + state } } diff --git a/src/main/scala/barstools/tapeout/transforms/GenerateTopAndHarness.scala b/src/main/scala/barstools/tapeout/transforms/GenerateTopAndHarness.scala index ef9c5408..4bf5bae5 100644 --- a/src/main/scala/barstools/tapeout/transforms/GenerateTopAndHarness.scala +++ b/src/main/scala/barstools/tapeout/transforms/GenerateTopAndHarness.scala @@ -8,6 +8,7 @@ import firrtl.options.{Dependency, InputAnnotationFileAnnotation, StageMain} import firrtl.passes.memlib.ReplSeqMemAnnotation import firrtl.stage.{FirrtlCircuitAnnotation, FirrtlStage, OutputFileAnnotation, RunFirrtlTransformAnnotation} import firrtl.transforms.BlackBoxResourceFileNameAnno +import barstools.floorplan.firrtl.{GenerateFloorplanIRPass, FloorplanIRFileAnnotation} import logger.LazyLogging // Requires two phases, one to collect modules below synTop in the hierarchy @@ -23,6 +24,8 @@ private class GenerateTopAndHarness(annotations: AnnotationSeq) extends LazyLogg val harnessOutput: Option[String] = annotations.collectFirst { case HarnessOutputAnnotation(h) => h } val topDotfOut: Option[String] = annotations.collectFirst { case TopDotfOutAnnotation(h) => h } val harnessDotfOut: Option[String] = annotations.collectFirst { case HarnessDotfOutAnnotation(h) => h } + // Note: this file name isn't actually used here, we just test to see if the annotaiton exists to run the pass + val floorplanFile: Option[String] = annotations.collectFirst { case FloorplanIRFileAnnotation(h) => h } val annoFiles: List[String] = annotations.flatMap { case InputAnnotationFileAnnotation(f) => Some(f) @@ -67,6 +70,7 @@ private class GenerateTopAndHarness(annotations: AnnotationSeq) extends LazyLogg RunFirrtlTransformAnnotation(Dependency[ReParentCircuit]), RunFirrtlTransformAnnotation(Dependency[RemoveUnusedModules]) ) ++ + floorplanFile.map(_ => RunFirrtlTransformAnnotation(new GenerateFloorplanIRPass)).toSeq ++ topAnnos ) annos.collectFirst { case FirrtlCircuitAnnotation(circuit) => circuit } match { From 10ac560bda1b06ae0ae9c7d050f58f2f74e2642c Mon Sep 17 00:00:00 2001 From: John Wright Date: Mon, 12 Jul 2021 00:56:08 -0700 Subject: [PATCH 23/73] chisel API cleanup --- floorplan/src/main/scala/IR.scala | 101 ++------ floorplan/src/main/scala/chisel/API.scala | 231 ++++++------------ .../src/main/scala/firrtl/Annotations.scala | 35 ++- floorplan/src/main/scala/firrtl/Passes.scala | 35 +-- 4 files changed, 119 insertions(+), 283 deletions(-) diff --git a/floorplan/src/main/scala/IR.scala b/floorplan/src/main/scala/IR.scala index 8dfe3240..7b9ebf9e 100644 --- a/floorplan/src/main/scala/IR.scala +++ b/floorplan/src/main/scala/IR.scala @@ -2,21 +2,33 @@ package barstools.floorplan // TODO Make some of this stuff private +// TODO make level an enum +// TODO add versioning to the FPIR file +// TODO clean up comment syntax, add scaladoc ////////////////////////////////////////////// Base classes sealed abstract class Element { - def name: String - // TODO make this an Enumeration def level: Int def serialize = FloorplanSerialization.serialize(this) - } sealed abstract class Primitive extends Element -////////////////////////////////////////////// Rect shape +sealed abstract class Group extends Element { + def elements: Seq[Option[String]] +} + +////////////////////////////////////////////// Hierarchical barrier + +private[floorplan] final case class HierarchicalBarrier( + name: String +) extends Primitive { + final def level = 0 // Maybe 1 +} + +////////////////////////////////////////////// Rectangular things trait ConstrainedRectLike { def width: Constraint[LengthUnit] @@ -31,13 +43,7 @@ trait ConcreteRectLike { } object IRLevel { - def max = 2 - -} - -sealed abstract class AbstractRectPrimitive extends Primitive { - final def level = 2 } sealed abstract class ConstrainedRectPrimitive extends Primitive with ConstrainedRectLike { @@ -56,40 +62,28 @@ private[floorplan] final case class ConstrainedDummyRect( aspectRatio: Constraint[Rational] = Unconstrained[Rational] ) extends ConstrainedRectPrimitive -sealed trait MacroLike - -private[floorplan] final case class AbstractMacro(name: String) extends AbstractRectPrimitive with MacroLike - -private[floorplan] final case class ConcreteMacro(name: String, width: LengthUnit, height: LengthUnit) extends ConcreteRectPrimitive with MacroLike - private[floorplan] final case class ConstrainedLogicRect( + name: String, width: Constraint[LengthUnit], height: Constraint[LengthUnit], area: Constraint[AreaUnit], aspectRatio: Constraint[Rational], hardBoundary: Boolean -) extends ConstrainedRectPrimitive { - def name = "" -} - -private[floorplan] final case class ConcreteLogicRect(width: LengthUnit, height: LengthUnit, hardBoundary: Boolean) extends ConcreteRectPrimitive { - def name = "" -} - -sealed abstract class Group extends Element { - - def elements: Seq[String] +) extends ConstrainedRectPrimitive - def mapElements(f: ((String, Int)) => String): Group +private[floorplan] final case class ConcreteLogicRect( + name: String, + width: LengthUnit, + height: LengthUnit, + hardBoundary: Boolean +) extends ConcreteRectPrimitive - protected def mapElementsHelper(f: ((String, Int)) => String): Seq[String] = elements.zipWithIndex.map(f) - -} +////////////////////////////////////////////// Aggregate (Group) things sealed abstract class Grid extends Group { def xDim: Int def yDim: Int - def elements: Seq[String] + def elements: Seq[Option[String]] assert(xDim > 0, "X dimension of grid must be positive") assert(yDim > 0, "Y dimension of grid must be positive") @@ -101,52 +95,11 @@ private[floorplan] final case class WeightedGrid( name: String, xDim: Int, yDim: Int, - elements: Seq[String], + elements: Seq[Option[String]], weights: Seq[Rational], packed: Boolean ) extends Grid { - - def level = 1 - - def mapElements(f: ((String, Int)) => String) = this.copy(name, xDim, yDim, mapElementsHelper(f), weights, packed) - -} - -/* -sealed abstract class Layout extends Group -sealed abstract class ConstrainedLayout extends Layout with ConstrainedRectLike -sealed abstract class ConcreteLayout extends Layout with ConcreteRectLike - -private[floorplan] final case class ConstrainedRatioLayout( - elements: Seq[String], - placements: Seq[RatioPlacementConstraint], - width: Constraint[LengthUnit] = Unconstrained[LengthUnit], - height: Constraint[LengthUnit] = Unconstrained[LengthUnit], - area: Constraint[AreaUnit] = Unconstrained[AreaUnit], - aspectRatio: Constraint[Rational] = Unconstrained[Rational] -) extends ConstrainedLayout { - def level = 1 - - def mapElements(f: ((String, Int)) => String) = this.copy(mapElementsHelper(f), placements, width, height, area, aspectRatio) - -} - -private[floorplan] final case class ConstrainedLengthLayout( - elements: Seq[String], - placements: Seq[LengthPlacementConstraint], - width: Constraint[LengthUnit] = Unconstrained[LengthUnit], - height: Constraint[LengthUnit] = Unconstrained[LengthUnit], - area: Constraint[AreaUnit] = Unconstrained[AreaUnit], - aspectRatio: Constraint[Rational] = Unconstrained[Rational] -) extends ConstrainedLayout { - - def level = 1 - - def mapElements(f: ((String, Int)) => String) = this.copy(mapElementsHelper(f), placements, width, height, area, aspectRatio) - } -*/ -// TODO add more layouts here diff --git a/floorplan/src/main/scala/chisel/API.scala b/floorplan/src/main/scala/chisel/API.scala index 985c9a89..0001098e 100644 --- a/floorplan/src/main/scala/chisel/API.scala +++ b/floorplan/src/main/scala/chisel/API.scala @@ -3,67 +3,50 @@ package barstools.floorplan.chisel import chisel3.{RawModule} -import firrtl.annotations.{ModuleTarget, InstanceTarget, Annotation} +import firrtl.annotations.{ReferenceTarget, InstanceTarget, Annotation} import barstools.floorplan._ -import barstools.floorplan.firrtl.{FloorplanAnnotation, FloorplanInstanceAnnotation, FloorplanModuleAnnotation, FloorplanGroupAnnotation} +import barstools.floorplan.firrtl.{FloorplanAnnotation, InstanceFloorplanAnnotation, NoReferenceFloorplanAnnotation} import scala.collection.mutable.{ArraySeq, ArrayBuffer, HashMap, Set, HashSet} final case class ChiselFloorplanException(message: String) extends Exception(message: String) -object Floorplan { +final class ChiselFloorplanContext private[chisel] (val root: InstanceTarget, topElement: ChiselElement) { - def rect[T <: RawModule](module: T, - width: Constraint[LengthUnit] = Unconstrained[LengthUnit], - height: Constraint[LengthUnit] = Unconstrained[LengthUnit], - area: Constraint[AreaUnit] = Unconstrained[AreaUnit], - aspectRatio: Constraint[Rational] = Unconstrained[Rational], - hardBoundary: Boolean = true - ) = { - val elt = new ChiselLogicRect(module, width, height, area, aspectRatio, hardBoundary) - FloorplanDatabase.register(module, elt) - elt - } + private[chisel] val elementBuf = new ArrayBuffer[ChiselElement]() - /* - def createRatioLayout[T <: RawModule](module: T, - width: Constraint[LengthUnit] = Unconstrained[LengthUnit], - height: Constraint[LengthUnit] = Unconstrained[LengthUnit], - area: Constraint[AreaUnit] = Unconstrained[AreaUnit], - aspectRatio: Constraint[Rational] = Unconstrained[Rational] - ) = { - val elt = new ChiselConstrainedRatioLayout(module, width, height, area, aspectRatio) - FloorplanDatabase.register(module, elt) - elt - } - */ + elementBuf.append(topElement) + + def elements: Seq[ChiselElement] = elementBuf.toSeq - /* - def createLengthLayout[T <: RawModule](module: T, + def createRect[T <: RawModule](module: T, width: Constraint[LengthUnit] = Unconstrained[LengthUnit], height: Constraint[LengthUnit] = Unconstrained[LengthUnit], area: Constraint[AreaUnit] = Unconstrained[AreaUnit], - aspectRatio: Constraint[Rational] = Unconstrained[Rational] - ) = { - val elt = new ChiselConstrainedLengthLayout(module, width, height, area, aspectRatio) - FloorplanDatabase.register(module, elt) - elt + aspectRatio: Constraint[Rational] = Unconstrained[Rational], + hardBoundary: Boolean = true + ) { + val inst: InstanceTarget = module.toAbsoluteTarget.asInstanceOf[InstanceTarget] + val name = FloorplanDatabase.getUnusedName(root, inst.instance) + val elt = new ChiselLogicRect(root, name, inst, width, height, area, aspectRatio, hardBoundary) + FloorplanDatabase.register(root, elt) + elementBuf.append(elt) } - */ - def dummy[T <: RawModule](module: T, + def createDummy( name: Option[String] = None, width: Constraint[LengthUnit] = Unconstrained[LengthUnit], height: Constraint[LengthUnit] = Unconstrained[LengthUnit], area: Constraint[AreaUnit] = Unconstrained[AreaUnit], aspectRatio: Constraint[Rational] = Unconstrained[Rational] - ) = { - val nameStr = name.getOrElse(FloorplanDatabase.getUnusedName(module)) - val elt = new ChiselDummyRect(module, nameStr, width, height, area, aspectRatio) - FloorplanDatabase.register(module, elt) - elt + ) { + val nameStr = FloorplanDatabase.getUnusedName(root, name) + val elt = new ChiselDummyRect(root, nameStr, width, height, area, aspectRatio) + FloorplanDatabase.register(root, elt) + elementBuf.append(elt) } +/* def grid[T <: RawModule](module: T, name: String, x: Int = 1, @@ -74,29 +57,51 @@ object Floorplan { FloorplanDatabase.register(module, elt) elt } + */ +} + +object Floorplan { + + def apply[T <: RawModule](module: T, + width: Constraint[LengthUnit] = Unconstrained[LengthUnit], + height: Constraint[LengthUnit] = Unconstrained[LengthUnit], + area: Constraint[AreaUnit] = Unconstrained[AreaUnit], + aspectRatio: Constraint[Rational] = Unconstrained[Rational], + hardBoundary: Boolean = true + ) = { + val root: InstanceTarget = module.toAbsoluteTarget.asInstanceOf[InstanceTarget] + val name = FloorplanDatabase.getUnusedName(root, root.ofModule) + val elt = new ChiselLogicRect(root, name, root, width, height, area, aspectRatio, hardBoundary) + FloorplanDatabase.register(root, elt) + new ChiselFloorplanContext(root, elt) + } } private[chisel] object FloorplanDatabase { - private val nameMap = new HashMap[RawModule, Set[String]]() + private val nameMap = new HashMap[InstanceTarget, Set[String]]() private val elements = new HashSet[ChiselElement]() - private def getSet(module: RawModule) = nameMap.getOrElseUpdate(module, new HashSet[String]) + private def getSet(root: InstanceTarget) = nameMap.getOrElseUpdate(root, new HashSet[String]) - def register(module: RawModule, element: ChiselElement): Unit = { + // TODO I'm not sure this is necessary anymore + def register(root: InstanceTarget, element: ChiselElement): Unit = { val name = element.name - val set = getSet(module) + val set = getSet(root) if (set.contains(name)) { - throw new ChiselFloorplanException(s"Duplicate floorplan element registration ${name} for module ${module.getClass.getName}!") + throw new ChiselFloorplanException(s"Duplicate floorplan element registration ${name} for InstanceTarget "+root.asPath.toList.map(_._1.value).mkString(".")+"!") } elements.add(element) set.add(name) } - def getUnusedName(module: RawModule, suggestion: String = "unnamed"): String = { - val set = getSet(module) + def getUnusedName(root: InstanceTarget, suggestion: Option[String]): String = getUnusedName(root, suggestion.getOrElse("unnamed")) + + def getUnusedName(root: InstanceTarget, suggestion: String): String = { + val set = getSet(root) var id = 0 + // This is slow and bad, but hopefully rare while (set.contains(suggestion + s"_${id}")) { id = id + 1 } suggestion + s"_${id}" } @@ -149,82 +154,56 @@ object FloorplanUnits { } -abstract class ChiselElement(val module: RawModule, val name: String) { - +sealed abstract class ChiselElement(val root: InstanceTarget, val name: String) { protected def generateElement(): Element - - // TODO if there are no other annotations, this separation may no longer be needed private[chisel] def getFloorplanAnnotations(): Seq[FloorplanAnnotation] - def getAnnotations(): Seq[Annotation] = getFloorplanAnnotations() - } -abstract class ChiselPrimitiveElement(module: RawModule, name: String) extends ChiselElement(module, name) { - - private[chisel] def getFloorplanAnnotations() = { - module.toTarget match { - case x: InstanceTarget => Seq(FloorplanInstanceAnnotation(x, generateElement())) - case x: ModuleTarget => Seq(FloorplanModuleAnnotation(x, generateElement())) - case _ => ??? - } - } - +sealed abstract class ChiselDummyElement(root: InstanceTarget, name: String) extends ChiselElement(root, name) { + private[chisel] def getFloorplanAnnotations() = Seq(NoReferenceFloorplanAnnotation(root, generateElement())) } +sealed abstract class ChiselPrimitiveElement(root: InstanceTarget, name: String, val instance: InstanceTarget) extends ChiselElement(root, name) { + private[chisel] def getFloorplanAnnotations() = Seq(InstanceFloorplanAnnotation(Seq(Seq(root), Seq(instance)), generateElement())) +} -abstract class ChiselGroupElement(module: RawModule, name: String) extends ChiselElement(module, name) { - +sealed abstract class ChiselGroupElement(root: InstanceTarget, name: String) extends ChiselElement(root, name) { protected def elements: Seq[Option[ChiselElement]] - - def isCommitted: Boolean - - private[chisel] def getFloorplanAnnotations(): Seq[FloorplanAnnotation] = { - // The head of elements will be this module, while the remaining ones are the children - val fpir = generateElement().asInstanceOf[Group] - val elementTargets = this.module.toTarget +: elements.map(_.get.module.toTarget) - val elementTargetsMapped: Seq[Seq[InstanceTarget]] = elementTargets.map { x => - assert(x.isInstanceOf[InstanceTarget], "All targets must be InstanceTargets for ChiselGroupElement annotations") - Seq(x.asInstanceOf[InstanceTarget]) - } - elements.foreach { x => assert(x.isDefined, "Elements must be defined before running getFloorplanAnnotations()") } - elements.flatMap(_.get.getFloorplanAnnotations()) :+ FloorplanGroupAnnotation(elementTargetsMapped, fpir) - } - + private[chisel] def getFloorplanAnnotations() = Seq(NoReferenceFloorplanAnnotation(root, generateElement())) } -/* -abstract class ChiselLayoutElement(module: RawModule) extends ChiselGroupElement(module, "") -*/ - final class ChiselLogicRect private[chisel] ( - module: RawModule, + root: InstanceTarget, + name: String, + instance: InstanceTarget, val width: Constraint[LengthUnit], val height: Constraint[LengthUnit], val area: Constraint[AreaUnit], val aspectRatio: Constraint[Rational], val hardBoundary: Boolean -) extends ChiselPrimitiveElement(module, "") { +) extends ChiselPrimitiveElement(root, name, instance) { - protected def generateElement(): Element = ConstrainedLogicRect(width, height, area, aspectRatio, hardBoundary) + protected def generateElement(): Element = ConstrainedLogicRect(name, width, height, area, aspectRatio, hardBoundary) } final class ChiselDummyRect private[chisel] ( - module: RawModule, + root: InstanceTarget, name: String, val width: Constraint[LengthUnit] = Unconstrained[LengthUnit], val height: Constraint[LengthUnit] = Unconstrained[LengthUnit], val area: Constraint[AreaUnit] = Unconstrained[AreaUnit], val aspectRatio: Constraint[Rational] = Unconstrained[Rational] -) extends ChiselPrimitiveElement(module, name) { +) extends ChiselDummyElement(root, name) { protected def generateElement(): Element = ConstrainedDummyRect(name, width, height, area, aspectRatio) } +/* final class ChiselWeightedGrid private[chisel] ( - module: RawModule, + root: InstanceTarget, name: String, val xDim: Int, val yDim: Int, @@ -258,7 +237,7 @@ final class ChiselWeightedGrid private[chisel] ( set(x, y, element, weight) } - def set(x: Int, y: Int, module: RawModule): Unit = set(x, y, module, Rational(1)) + def set(x: Int, y: Int, root: InstanceTarget): Unit = set(x, y, module, Rational(1)) protected def generateElement(): Element = { _isCommitted = true @@ -272,76 +251,6 @@ final class ChiselWeightedGrid private[chisel] ( packed) } -} - -/* -final class ChiselConstrainedRatioLayout private[chisel] ( - module: RawModule, - val width: Constraint[LengthUnit] = Unconstrained[LengthUnit], - val height: Constraint[LengthUnit] = Unconstrained[LengthUnit], - val area: Constraint[AreaUnit] = Unconstrained[AreaUnit], - val aspectRatio: Constraint[Rational] = Unconstrained[Rational] -) extends ChiselLayoutElement(module) { - - protected val elements = new ArrayBuffer[Option[ChiselElement]]() - protected val placements = new ArrayBuffer[RatioPlacementConstraint]() - - def add(element: ChiselElement, constraint: RatioPlacementConstraint): Unit = { - if (isCommitted) throw new ChiselFloorplanException("Cannot modify a ChiselConstrainedPlacement after committing") - elements.append(Some(element)) - placements.append(constraint) - } - - def add(element: ChiselElement, x: Constraint[Rational], y: Constraint[Rational], anchor: PlacementAnchor = LowerLeft()): Unit = add(element, RatioPlacementConstraint(x, y, anchor)) - - def addModule(elementModule: RawModule, constraint: RatioPlacementConstraint): Unit = add(Floorplan.createRect(elementModule), constraint) - def addModule(elementModule: RawModule, x: Constraint[Rational], y: Constraint[Rational], anchor: PlacementAnchor = LowerLeft()): Unit = addModule(elementModule, RatioPlacementConstraint(x, y, anchor)) - - protected def generateElement(): Element = { - ConstrainedRatioLayout( - elements.map(_.get.commit()), - placements, - width, - height, - area, - aspectRatio) - } - } */ -/* -final class ChiselConstrainedLengthLayout private[chisel] ( - module: RawModule, - val width: Constraint[LengthUnit] = Unconstrained[LengthUnit], - val height: Constraint[LengthUnit] = Unconstrained[LengthUnit], - val area: Constraint[AreaUnit] = Unconstrained[AreaUnit], - val aspectRatio: Constraint[Rational] = Unconstrained[Rational] -) extends ChiselLayoutElement(module) { - - protected val elements = new ArrayBuffer[Option[ChiselElement]]() - protected val placements = new ArrayBuffer[LengthPlacementConstraint]() - - def add(element: ChiselElement, constraint: LengthPlacementConstraint): Unit = { - if (isCommitted) throw new ChiselFloorplanException("Cannot modify a ChiselConstrainedPlacement after committing") - elements.append(Some(element)) - placements.append(constraint) - } - - def add(element: ChiselElement, x: Constraint[LengthUnit], y: Constraint[LengthUnit], anchor: PlacementAnchor = LowerLeft()): Unit = add(element, LengthPlacementConstraint(x, y, anchor)) - - def addModule(elementModule: RawModule, constraint: LengthPlacementConstraint): Unit = add(Floorplan.createRect(elementModule), constraint) - def addModule(elementModule: RawModule, x: Constraint[LengthUnit], y: Constraint[LengthUnit], anchor: PlacementAnchor = LowerLeft()): Unit = addModule(elementModule, LengthPlacementConstraint(x, y, anchor)) - - protected def generateElement(): Element = { - ConstrainedLengthLayout( - elements.map(_.get.commit()), - placements, - width, - height, - area, - aspectRatio) - } - -} -*/ diff --git a/floorplan/src/main/scala/firrtl/Annotations.scala b/floorplan/src/main/scala/firrtl/Annotations.scala index 253eaa0c..bd9a87c0 100644 --- a/floorplan/src/main/scala/firrtl/Annotations.scala +++ b/floorplan/src/main/scala/firrtl/Annotations.scala @@ -6,41 +6,36 @@ import barstools.floorplan.{Element, Group} import firrtl.annotations._ import firrtl.stage.{RunFirrtlTransformAnnotation} -// John: Right now we are using ModuleTarget, which will mean that all instances of the same master -// will have the same floorplan. This is probably OK for now, but eventually we will want to support -// instance targets as well, which have some deduping issues. +// John '21: We're going to get rid of ModuleTarget support for now. InstanceTargets may break dedup, but they don't support heterogeneous configs and are +// kind of redundant with InstanceTargets. InstanceTarget behavior is a little more intuitive for writing the Aspects. -// John: Another note. To make this a bit easier, I'm going to make floorplan IR embedded in this annotation rather than relying on +// John '20: To make this a bit easier, I'm going to make floorplan IR embedded in this annotation rather than relying on // the annotation to serialize the case class correctly (it doesn't currently serialize type parameters, which makes this a bit painful) // We'll probably want to change this later trait FloorplanAnnotation extends Annotation { val fpir: String } -case class FloorplanModuleAnnotation(target: ModuleTarget, fpir: String) extends SingleTargetAnnotation[ModuleTarget] with FloorplanAnnotation { - def duplicate(t: ModuleTarget) = this.copy(t, fpir) -} +case class InstanceFloorplanAnnotation(targets: Seq[Seq[Target]], fpir: String) extends MultiTargetAnnotation with FloorplanAnnotation { -case class FloorplanInstanceAnnotation(target: InstanceTarget, fpir: String) extends SingleTargetAnnotation[InstanceTarget] with FloorplanAnnotation { - def duplicate(t: InstanceTarget) = this.copy(t, fpir) -} + assert(targets.length == 2, "InstanceFloorplanAnnotation requires 2 targets") + //assert(targets.flatten.filterNot(_.isInstanceOf[InstanceTarget]).isEmpty, "InstanceFloorplanAnnotation requires InstanceTarget targets") -case class FloorplanGroupAnnotation(targets: Seq[Seq[Target]], fpir: String) extends MultiTargetAnnotation with FloorplanAnnotation { - def duplicate(t: Seq[Seq[Target]]) = this.copy(t, fpir) + def duplicate(t: Seq[Seq[Target]]) = { + this.copy(t, fpir) + } } -object FloorplanModuleAnnotation { - def apply(target: ModuleTarget, element: Element): FloorplanModuleAnnotation = FloorplanModuleAnnotation(target, element.serialize) +case class NoReferenceFloorplanAnnotation(target: InstanceTarget, fpir: String) extends SingleTargetAnnotation[InstanceTarget] with FloorplanAnnotation { + def duplicate(t: InstanceTarget) = this.copy(t, fpir) } -object FloorplanInstanceAnnotation { - def apply(target: InstanceTarget, element: Element): FloorplanInstanceAnnotation = FloorplanInstanceAnnotation(target, element.serialize) +object InstanceFloorplanAnnotation { + def apply(targets: Seq[Seq[Target]], element: Element): InstanceFloorplanAnnotation = InstanceFloorplanAnnotation(targets, element.serialize) } -object FloorplanGroupAnnotation { - //def apply(targets: Seq[Target], fpir: String): FloorplanGroupAnnotation = FloorplanGroupAnnotation(targets.map(Seq(_)), fpir) - //def apply(targets: Seq[Target], element: Element): FloorplanGroupAnnotation = FloorplanGroupAnnotation(targets, element.serialize) - def apply(targets: Seq[Seq[InstanceTarget]], element: Group): FloorplanGroupAnnotation = FloorplanGroupAnnotation(targets, element.serialize) +object NoReferenceFloorplanAnnotation { + def apply(target: InstanceTarget, element: Element): NoReferenceFloorplanAnnotation = NoReferenceFloorplanAnnotation(target, element.serialize) } case class FloorplanIRFileAnnotation(value: String) extends NoTargetAnnotation diff --git a/floorplan/src/main/scala/firrtl/Passes.scala b/floorplan/src/main/scala/firrtl/Passes.scala index 622e2277..daf9b841 100644 --- a/floorplan/src/main/scala/firrtl/Passes.scala +++ b/floorplan/src/main/scala/firrtl/Passes.scala @@ -5,7 +5,7 @@ import barstools.floorplan.{FloorplanSerialization, FloorplanElementRecord, Floo import firrtl.{CircuitState, Namespace, Transform, AnnotationSeq, VerilogEmitter, DependencyAPIMigration} import firrtl.options.{Dependency, RegisteredTransform, ShellOption} import firrtl.analyses.{InstanceKeyGraph} -import firrtl.annotations.{InstanceTarget, ModuleTarget} +import firrtl.annotations.{InstanceTarget} // NOTE: If you rename/add this transform, don't forget to update META-INF // See the @note in the RegisteredTransform documentation @@ -34,37 +34,16 @@ class GenerateFloorplanIRPass extends Transform with RegisteredTransform with De def execute(state: CircuitState): CircuitState = { - // TODO don't need graph if there are no annos, which can be a speedup - val graph = InstanceKeyGraph(state.circuit) - def getPaths(name: String): Seq[String] = getInstancePathsFromGraph(graph, state.circuit.main, name) - def getInstancePath(t: InstanceTarget): String = { - val result = getPaths(t.module).map(_ + (t.path.toList.map(_._1.value) :+ t.instance mkString ".")) - if (result.size > 1) throw new Exception(s"Too many instances for InstanceTarget ${t}! Fix me!") - if (result.size == 0) throw new Exception(s"InstanceTarget ${t} does not exist!") - result(0) - } + def getInstancePath(t: InstanceTarget): String = t.asPath.toList.map(_._1.value).mkString(".") def newRecord(path: String, anno: FloorplanAnnotation) = FloorplanElementRecord(path, FloorplanSerialization.deserialize(anno.fpir)) val list = state.annotations.collect({ - case x: FloorplanInstanceAnnotation => - throw new Exception("Don't use this yet") - //Seq(newRecord(getInstancePath(x.target), x)) - case x: FloorplanModuleAnnotation => - getPaths(x.target.name).map(newRecord(_, x)) - case x: FloorplanGroupAnnotation => { - throw new Exception("Don't use this yet") - /* - val paths = x.targets.map(_(0).asInstanceOf[InstanceTarget]).map(getInstancePath) - // paths(0) is special; it's the path to the module the element is attached to - val element = FloorplanSerialization. - deserialize(x.fpir). - asInstanceOf[barstools.floorplan.Group]. - mapElements { case (name, id) => paths(id + 1) + (if (name == "") "" else "#" + name) } - Seq(FloorplanElementRecord(paths(0), element)) - */ - } - }).flatten + case x: NoReferenceFloorplanAnnotation => + newRecord(getInstancePath(x.target), x) + case x: InstanceFloorplanAnnotation if x.targets.flatten.length > 0 => + newRecord(getInstancePath(x.targets(0)(0).asInstanceOf[InstanceTarget]), x) + }) val filename = state.annotations.collectFirst({ case x: FloorplanIRFileAnnotation => x.value From 5a98ec0e71fdbd51aa55e5b96889dce1a99f9d77 Mon Sep 17 00:00:00 2001 From: John Wright Date: Mon, 12 Jul 2021 02:09:17 -0700 Subject: [PATCH 24/73] Fix relative pathing --- floorplan/src/main/scala/FloorplanState.scala | 2 +- floorplan/src/main/scala/chisel/API.scala | 6 +++-- .../main/scala/compiler/FloorplanTree.scala | 6 +++-- floorplan/src/main/scala/firrtl/Passes.scala | 24 +++++++++---------- 4 files changed, 21 insertions(+), 17 deletions(-) diff --git a/floorplan/src/main/scala/FloorplanState.scala b/floorplan/src/main/scala/FloorplanState.scala index fca49dda..55ded412 100644 --- a/floorplan/src/main/scala/FloorplanState.scala +++ b/floorplan/src/main/scala/FloorplanState.scala @@ -1,7 +1,7 @@ // See LICENSE for license details package barstools.floorplan -final case class FloorplanElementRecord(path: String, element: Element) +final case class FloorplanElementRecord(root: String, inst: Option[String], element: Element) final case class FloorplanState(elements: Seq[FloorplanElementRecord], level: Int) diff --git a/floorplan/src/main/scala/chisel/API.scala b/floorplan/src/main/scala/chisel/API.scala index 0001098e..0fac838c 100644 --- a/floorplan/src/main/scala/chisel/API.scala +++ b/floorplan/src/main/scala/chisel/API.scala @@ -25,12 +25,13 @@ final class ChiselFloorplanContext private[chisel] (val root: InstanceTarget, to area: Constraint[AreaUnit] = Unconstrained[AreaUnit], aspectRatio: Constraint[Rational] = Unconstrained[Rational], hardBoundary: Boolean = true - ) { + ): ChiselElement = { val inst: InstanceTarget = module.toAbsoluteTarget.asInstanceOf[InstanceTarget] val name = FloorplanDatabase.getUnusedName(root, inst.instance) val elt = new ChiselLogicRect(root, name, inst, width, height, area, aspectRatio, hardBoundary) FloorplanDatabase.register(root, elt) elementBuf.append(elt) + elt } def createDummy( @@ -39,11 +40,12 @@ final class ChiselFloorplanContext private[chisel] (val root: InstanceTarget, to height: Constraint[LengthUnit] = Unconstrained[LengthUnit], area: Constraint[AreaUnit] = Unconstrained[AreaUnit], aspectRatio: Constraint[Rational] = Unconstrained[Rational] - ) { + ): ChiselElement = { val nameStr = FloorplanDatabase.getUnusedName(root, name) val elt = new ChiselDummyRect(root, nameStr, width, height, area, aspectRatio) FloorplanDatabase.register(root, elt) elementBuf.append(elt) + elt } /* diff --git a/floorplan/src/main/scala/compiler/FloorplanTree.scala b/floorplan/src/main/scala/compiler/FloorplanTree.scala index e931f8e9..07f3ac56 100644 --- a/floorplan/src/main/scala/compiler/FloorplanTree.scala +++ b/floorplan/src/main/scala/compiler/FloorplanTree.scala @@ -4,6 +4,8 @@ package barstools.floorplan.compiler import scala.collection.mutable.{HashMap} import barstools.floorplan.{Element, FloorplanState, FloorplanElementRecord} +// TODO all of this is out of date + class FloorplanTree(state: FloorplanState) { class Node(val parent: Option[Node], val name: String) { @@ -68,7 +70,7 @@ class FloorplanTree(state: FloorplanState) { } state.elements.foreach { record => - val (path, tag) = parsePathAndTag(record.path) + val (path, tag) = parsePathAndTag(record.root) root.addElement(path, tag, record.element) } @@ -85,7 +87,7 @@ class FloorplanTree(state: FloorplanState) { } def toState: FloorplanState = { - val records = root.getElements.map { case (path, elt) => FloorplanElementRecord(path, elt) } + val records = root.getElements.map { case (path, elt) => FloorplanElementRecord(path, None, elt) } // TODO this is broken but unused right now FloorplanState(records, records.map(_.element.level).max) } diff --git a/floorplan/src/main/scala/firrtl/Passes.scala b/floorplan/src/main/scala/firrtl/Passes.scala index daf9b841..ccf4482e 100644 --- a/floorplan/src/main/scala/firrtl/Passes.scala +++ b/floorplan/src/main/scala/firrtl/Passes.scala @@ -24,25 +24,25 @@ class GenerateFloorplanIRPass extends Transform with RegisteredTransform with De ) ) - private def getInstancePathsFromGraph(graph: InstanceKeyGraph, cktName: String, name: String): Seq[String] = { - if (cktName == name) { - Seq("") - } else { - graph.findInstancesInHierarchy(name).map(_.map(_.name).mkString(".") + ".") - } - } + def execute(state: CircuitState): CircuitState = { + def getInstancePath(t: InstanceTarget): String = "/" + t.asPath.toList.map(_._1.value).mkString("/") - def execute(state: CircuitState): CircuitState = { + def getRelativePath(root: InstanceTarget, inst: InstanceTarget): String = { + val rootPath = root.asPath + val instPath = inst.asPath + assert(instPath.take(rootPath.length) == rootPath, s"InstanceTarget ${instPath} must be inside ${rootPath}") + instPath.drop(rootPath.length).toList.map(_._1.value).mkString("/") + } - def getInstancePath(t: InstanceTarget): String = t.asPath.toList.map(_._1.value).mkString(".") - def newRecord(path: String, anno: FloorplanAnnotation) = FloorplanElementRecord(path, FloorplanSerialization.deserialize(anno.fpir)) + def newRecord(path: String, ref: Option[String], anno: FloorplanAnnotation) = FloorplanElementRecord(path, ref, FloorplanSerialization.deserialize(anno.fpir)) val list = state.annotations.collect({ case x: NoReferenceFloorplanAnnotation => - newRecord(getInstancePath(x.target), x) + newRecord(getInstancePath(x.target), None, x) case x: InstanceFloorplanAnnotation if x.targets.flatten.length > 0 => - newRecord(getInstancePath(x.targets(0)(0).asInstanceOf[InstanceTarget]), x) + newRecord(getInstancePath(x.targets(0)(0).asInstanceOf[InstanceTarget]), + Some(getRelativePath(x.targets(0)(0).asInstanceOf[InstanceTarget], x.targets(1)(0).asInstanceOf[InstanceTarget])), x) }) val filename = state.annotations.collectFirst({ From 4a8a533ee1d1c4301a32e9c7acef8668ebb17188 Mon Sep 17 00:00:00 2001 From: John Wright Date: Mon, 12 Jul 2021 22:37:33 -0700 Subject: [PATCH 25/73] Start adding Mem support --- floorplan/src/main/scala/IR.scala | 23 +++++++++++++ floorplan/src/main/scala/chisel/API.scala | 34 ++++++++++++++++--- .../src/main/scala/firrtl/Annotations.scala | 22 +++++++++++- floorplan/src/main/scala/firrtl/Passes.scala | 16 ++++++--- 4 files changed, 86 insertions(+), 9 deletions(-) diff --git a/floorplan/src/main/scala/IR.scala b/floorplan/src/main/scala/IR.scala index 7b9ebf9e..b99b6f5a 100644 --- a/floorplan/src/main/scala/IR.scala +++ b/floorplan/src/main/scala/IR.scala @@ -103,3 +103,26 @@ private[floorplan] final case class WeightedGrid( } +////////////////////////////////////////////// MemElements and Macros + +// Reference to a MemElement +private[floorplan] final case class MemElement( + name: String +) extends Primitive { + def level = 2 +} + +// Container for MemElements +private[floorplan] final case class MemElementArray( + name: String, + elements: Seq[Option[String]] +) extends Group { + def level = 2 +} + +private[floorplan] final case class ConcreteMacro ( + name: String, + width: LengthUnit, + height: LengthUnit +) extends ConcreteRectPrimitive + diff --git a/floorplan/src/main/scala/chisel/API.scala b/floorplan/src/main/scala/chisel/API.scala index 0fac838c..6a0a2b8f 100644 --- a/floorplan/src/main/scala/chisel/API.scala +++ b/floorplan/src/main/scala/chisel/API.scala @@ -3,10 +3,10 @@ package barstools.floorplan.chisel import chisel3.{RawModule} -import firrtl.annotations.{ReferenceTarget, InstanceTarget, Annotation} +import firrtl.annotations.{ReferenceTarget, InstanceTarget, Target, Annotation} import barstools.floorplan._ -import barstools.floorplan.firrtl.{FloorplanAnnotation, InstanceFloorplanAnnotation, NoReferenceFloorplanAnnotation} +import barstools.floorplan.firrtl.{FloorplanAnnotation, ReferenceFloorplanAnnotation, InstanceFloorplanAnnotation, NoReferenceFloorplanAnnotation} import scala.collection.mutable.{ArraySeq, ArrayBuffer, HashMap, Set, HashSet} final case class ChiselFloorplanException(message: String) extends Exception(message: String) @@ -48,6 +48,17 @@ final class ChiselFloorplanContext private[chisel] (val root: InstanceTarget, to elt } + def addMem[T <: chisel3.Data]( + mem: chisel3.MemBase[T] + ): ChiselElement = { + val ref = mem.toAbsoluteTarget + val name = FloorplanDatabase.getUnusedName(root, ref.ref) + val elt = new ChiselMemElement(root, name, ref) + FloorplanDatabase.register(root, elt) + elementBuf.append(elt) + elt + } + /* def grid[T <: RawModule](module: T, name: String, @@ -166,10 +177,14 @@ sealed abstract class ChiselDummyElement(root: InstanceTarget, name: String) ext private[chisel] def getFloorplanAnnotations() = Seq(NoReferenceFloorplanAnnotation(root, generateElement())) } -sealed abstract class ChiselPrimitiveElement(root: InstanceTarget, name: String, val instance: InstanceTarget) extends ChiselElement(root, name) { +sealed abstract class ChiselInstanceElement(root: InstanceTarget, name: String, val instance: InstanceTarget) extends ChiselElement(root, name) { private[chisel] def getFloorplanAnnotations() = Seq(InstanceFloorplanAnnotation(Seq(Seq(root), Seq(instance)), generateElement())) } +sealed abstract class ChiselReferenceElement(root: InstanceTarget, name: String, val reference: ReferenceTarget) extends ChiselElement(root, name) { + private[chisel] def getFloorplanAnnotations() = Seq(ReferenceFloorplanAnnotation(Seq(Seq(root), Seq(reference)), generateElement())) +} + sealed abstract class ChiselGroupElement(root: InstanceTarget, name: String) extends ChiselElement(root, name) { protected def elements: Seq[Option[ChiselElement]] private[chisel] def getFloorplanAnnotations() = Seq(NoReferenceFloorplanAnnotation(root, generateElement())) @@ -184,7 +199,7 @@ final class ChiselLogicRect private[chisel] ( val area: Constraint[AreaUnit], val aspectRatio: Constraint[Rational], val hardBoundary: Boolean -) extends ChiselPrimitiveElement(root, name, instance) { +) extends ChiselInstanceElement(root, name, instance) { protected def generateElement(): Element = ConstrainedLogicRect(name, width, height, area, aspectRatio, hardBoundary) @@ -203,6 +218,17 @@ final class ChiselDummyRect private[chisel] ( } +final class ChiselMemElement private[chisel] ( + root: InstanceTarget, + name: String, + reference: ReferenceTarget +) extends ChiselReferenceElement(root, name, reference) { + + protected def generateElement(): Element = MemElement(name) + +} + + /* final class ChiselWeightedGrid private[chisel] ( root: InstanceTarget, diff --git a/floorplan/src/main/scala/firrtl/Annotations.scala b/floorplan/src/main/scala/firrtl/Annotations.scala index bd9a87c0..e81af4e3 100644 --- a/floorplan/src/main/scala/firrtl/Annotations.scala +++ b/floorplan/src/main/scala/firrtl/Annotations.scala @@ -18,8 +18,24 @@ trait FloorplanAnnotation extends Annotation { case class InstanceFloorplanAnnotation(targets: Seq[Seq[Target]], fpir: String) extends MultiTargetAnnotation with FloorplanAnnotation { +/* assert(targets.length == 2, "InstanceFloorplanAnnotation requires 2 targets") - //assert(targets.flatten.filterNot(_.isInstanceOf[InstanceTarget]).isEmpty, "InstanceFloorplanAnnotation requires InstanceTarget targets") + assert(targets(0)(0).isInstanceOf[InstanceTarget], "Root must be an InstanceTarget") + assert(targets(1)(0).isInstanceOf[InstanceTarget], "Instance must be an InstanceTarget") +*/ + + def duplicate(t: Seq[Seq[Target]]) = { + this.copy(t, fpir) + } +} + +case class ReferenceFloorplanAnnotation(targets: Seq[Seq[Target]], fpir: String) extends MultiTargetAnnotation with FloorplanAnnotation { + +/* + assert(targets.length == 2, "InstanceFloorplanAnnotation requires 2 targets") + assert(targets(0)(0).isInstanceOf[InstanceTarget], "Root must be an InstanceTarget") + assert(targets(1)(0).isInstanceOf[ReferenceTarget], "Ref must be an ReferenceTarget") +*/ def duplicate(t: Seq[Seq[Target]]) = { this.copy(t, fpir) @@ -34,6 +50,10 @@ object InstanceFloorplanAnnotation { def apply(targets: Seq[Seq[Target]], element: Element): InstanceFloorplanAnnotation = InstanceFloorplanAnnotation(targets, element.serialize) } +object ReferenceFloorplanAnnotation { + def apply(targets: Seq[Seq[Target]], element: Element): ReferenceFloorplanAnnotation = ReferenceFloorplanAnnotation(targets, element.serialize) +} + object NoReferenceFloorplanAnnotation { def apply(target: InstanceTarget, element: Element): NoReferenceFloorplanAnnotation = NoReferenceFloorplanAnnotation(target, element.serialize) } diff --git a/floorplan/src/main/scala/firrtl/Passes.scala b/floorplan/src/main/scala/firrtl/Passes.scala index ccf4482e..fd0a599c 100644 --- a/floorplan/src/main/scala/firrtl/Passes.scala +++ b/floorplan/src/main/scala/firrtl/Passes.scala @@ -5,7 +5,7 @@ import barstools.floorplan.{FloorplanSerialization, FloorplanElementRecord, Floo import firrtl.{CircuitState, Namespace, Transform, AnnotationSeq, VerilogEmitter, DependencyAPIMigration} import firrtl.options.{Dependency, RegisteredTransform, ShellOption} import firrtl.analyses.{InstanceKeyGraph} -import firrtl.annotations.{InstanceTarget} +import firrtl.annotations.{InstanceTarget, ReferenceTarget, IsComponent} // NOTE: If you rename/add this transform, don't forget to update META-INF // See the @note in the RegisteredTransform documentation @@ -28,11 +28,16 @@ class GenerateFloorplanIRPass extends Transform with RegisteredTransform with De def getInstancePath(t: InstanceTarget): String = "/" + t.asPath.toList.map(_._1.value).mkString("/") - def getRelativePath(root: InstanceTarget, inst: InstanceTarget): String = { + def getRelativePath(root: InstanceTarget, inst: IsComponent): String = { val rootPath = root.asPath val instPath = inst.asPath assert(instPath.take(rootPath.length) == rootPath, s"InstanceTarget ${instPath} must be inside ${rootPath}") - instPath.drop(rootPath.length).toList.map(_._1.value).mkString("/") + val pathStr = instPath.drop(rootPath.length).toList.map(_._1.value).mkString("/") + inst match { + case x: InstanceTarget => pathStr + case x: ReferenceTarget => pathStr + "." + x.ref + case _ => ??? // Shouldn't exist + } } def newRecord(path: String, ref: Option[String], anno: FloorplanAnnotation) = FloorplanElementRecord(path, ref, FloorplanSerialization.deserialize(anno.fpir)) @@ -40,9 +45,12 @@ class GenerateFloorplanIRPass extends Transform with RegisteredTransform with De val list = state.annotations.collect({ case x: NoReferenceFloorplanAnnotation => newRecord(getInstancePath(x.target), None, x) - case x: InstanceFloorplanAnnotation if x.targets.flatten.length > 0 => + case x: InstanceFloorplanAnnotation if x.targets.flatten.length == 2 => newRecord(getInstancePath(x.targets(0)(0).asInstanceOf[InstanceTarget]), Some(getRelativePath(x.targets(0)(0).asInstanceOf[InstanceTarget], x.targets(1)(0).asInstanceOf[InstanceTarget])), x) + case x: ReferenceFloorplanAnnotation if x.targets.flatten.length == 2 => + newRecord(getInstancePath(x.targets(0)(0).asInstanceOf[InstanceTarget]), + Some(getRelativePath(x.targets(0)(0).asInstanceOf[InstanceTarget], x.targets(1)(0).asInstanceOf[ReferenceTarget])), x) }) val filename = state.annotations.collectFirst({ From d580f7abd10cdcaec5ee440ef148a1e5f680dc6e Mon Sep 17 00:00:00 2001 From: John Wright Date: Mon, 12 Jul 2021 23:01:36 -0700 Subject: [PATCH 26/73] Style --- .../src/main/scala/firrtl/Annotations.scala | 22 ++----------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/floorplan/src/main/scala/firrtl/Annotations.scala b/floorplan/src/main/scala/firrtl/Annotations.scala index e81af4e3..218acc73 100644 --- a/floorplan/src/main/scala/firrtl/Annotations.scala +++ b/floorplan/src/main/scala/firrtl/Annotations.scala @@ -17,29 +17,11 @@ trait FloorplanAnnotation extends Annotation { } case class InstanceFloorplanAnnotation(targets: Seq[Seq[Target]], fpir: String) extends MultiTargetAnnotation with FloorplanAnnotation { - -/* - assert(targets.length == 2, "InstanceFloorplanAnnotation requires 2 targets") - assert(targets(0)(0).isInstanceOf[InstanceTarget], "Root must be an InstanceTarget") - assert(targets(1)(0).isInstanceOf[InstanceTarget], "Instance must be an InstanceTarget") -*/ - - def duplicate(t: Seq[Seq[Target]]) = { - this.copy(t, fpir) - } + def duplicate(t: Seq[Seq[Target]]) = this.copy(t, fpir) } case class ReferenceFloorplanAnnotation(targets: Seq[Seq[Target]], fpir: String) extends MultiTargetAnnotation with FloorplanAnnotation { - -/* - assert(targets.length == 2, "InstanceFloorplanAnnotation requires 2 targets") - assert(targets(0)(0).isInstanceOf[InstanceTarget], "Root must be an InstanceTarget") - assert(targets(1)(0).isInstanceOf[ReferenceTarget], "Ref must be an ReferenceTarget") -*/ - - def duplicate(t: Seq[Seq[Target]]) = { - this.copy(t, fpir) - } + def duplicate(t: Seq[Seq[Target]]) = this.copy(t, fpir) } case class NoReferenceFloorplanAnnotation(target: InstanceTarget, fpir: String) extends SingleTargetAnnotation[InstanceTarget] with FloorplanAnnotation { From 471e7a01bdee5afcb1717ede8e19e9d704acc3cf Mon Sep 17 00:00:00 2001 From: John Wright Date: Tue, 13 Jul 2021 11:37:06 -0700 Subject: [PATCH 27/73] Handle ReplSeqMem in the records --- floorplan/src/main/scala/FloorplanState.scala | 2 +- floorplan/src/main/scala/chisel/API.scala | 12 +++---- .../src/main/scala/firrtl/Annotations.scala | 6 ++-- floorplan/src/main/scala/firrtl/Passes.scala | 34 ++++++++++++++----- 4 files changed, 36 insertions(+), 18 deletions(-) diff --git a/floorplan/src/main/scala/FloorplanState.scala b/floorplan/src/main/scala/FloorplanState.scala index 55ded412..769f0731 100644 --- a/floorplan/src/main/scala/FloorplanState.scala +++ b/floorplan/src/main/scala/FloorplanState.scala @@ -1,7 +1,7 @@ // See LICENSE for license details package barstools.floorplan -final case class FloorplanElementRecord(root: String, inst: Option[String], element: Element) +final case class FloorplanElementRecord(root: String, inst: Option[String], element: Element, memExt: Option[String] = None) final case class FloorplanState(elements: Seq[FloorplanElementRecord], level: Int) diff --git a/floorplan/src/main/scala/chisel/API.scala b/floorplan/src/main/scala/chisel/API.scala index 6a0a2b8f..b1ec1858 100644 --- a/floorplan/src/main/scala/chisel/API.scala +++ b/floorplan/src/main/scala/chisel/API.scala @@ -6,7 +6,7 @@ import chisel3.{RawModule} import firrtl.annotations.{ReferenceTarget, InstanceTarget, Target, Annotation} import barstools.floorplan._ -import barstools.floorplan.firrtl.{FloorplanAnnotation, ReferenceFloorplanAnnotation, InstanceFloorplanAnnotation, NoReferenceFloorplanAnnotation} +import barstools.floorplan.firrtl.{FloorplanAnnotation, MemFloorplanAnnotation, InstanceFloorplanAnnotation, NoReferenceFloorplanAnnotation} import scala.collection.mutable.{ArraySeq, ArrayBuffer, HashMap, Set, HashSet} final case class ChiselFloorplanException(message: String) extends Exception(message: String) @@ -53,7 +53,7 @@ final class ChiselFloorplanContext private[chisel] (val root: InstanceTarget, to ): ChiselElement = { val ref = mem.toAbsoluteTarget val name = FloorplanDatabase.getUnusedName(root, ref.ref) - val elt = new ChiselMemElement(root, name, ref) + val elt = new ChiselMem(root, name, ref) FloorplanDatabase.register(root, elt) elementBuf.append(elt) elt @@ -181,8 +181,8 @@ sealed abstract class ChiselInstanceElement(root: InstanceTarget, name: String, private[chisel] def getFloorplanAnnotations() = Seq(InstanceFloorplanAnnotation(Seq(Seq(root), Seq(instance)), generateElement())) } -sealed abstract class ChiselReferenceElement(root: InstanceTarget, name: String, val reference: ReferenceTarget) extends ChiselElement(root, name) { - private[chisel] def getFloorplanAnnotations() = Seq(ReferenceFloorplanAnnotation(Seq(Seq(root), Seq(reference)), generateElement())) +sealed abstract class ChiselMemElement(root: InstanceTarget, name: String, val reference: ReferenceTarget) extends ChiselElement(root, name) { + private[chisel] def getFloorplanAnnotations() = Seq(MemFloorplanAnnotation(Seq(Seq(root), Seq(reference)), generateElement())) } sealed abstract class ChiselGroupElement(root: InstanceTarget, name: String) extends ChiselElement(root, name) { @@ -218,11 +218,11 @@ final class ChiselDummyRect private[chisel] ( } -final class ChiselMemElement private[chisel] ( +final class ChiselMem private[chisel] ( root: InstanceTarget, name: String, reference: ReferenceTarget -) extends ChiselReferenceElement(root, name, reference) { +) extends ChiselMemElement(root, name, reference) { protected def generateElement(): Element = MemElement(name) diff --git a/floorplan/src/main/scala/firrtl/Annotations.scala b/floorplan/src/main/scala/firrtl/Annotations.scala index 218acc73..75b4cceb 100644 --- a/floorplan/src/main/scala/firrtl/Annotations.scala +++ b/floorplan/src/main/scala/firrtl/Annotations.scala @@ -20,7 +20,7 @@ case class InstanceFloorplanAnnotation(targets: Seq[Seq[Target]], fpir: String) def duplicate(t: Seq[Seq[Target]]) = this.copy(t, fpir) } -case class ReferenceFloorplanAnnotation(targets: Seq[Seq[Target]], fpir: String) extends MultiTargetAnnotation with FloorplanAnnotation { +case class MemFloorplanAnnotation(targets: Seq[Seq[Target]], fpir: String) extends MultiTargetAnnotation with FloorplanAnnotation { def duplicate(t: Seq[Seq[Target]]) = this.copy(t, fpir) } @@ -32,8 +32,8 @@ object InstanceFloorplanAnnotation { def apply(targets: Seq[Seq[Target]], element: Element): InstanceFloorplanAnnotation = InstanceFloorplanAnnotation(targets, element.serialize) } -object ReferenceFloorplanAnnotation { - def apply(targets: Seq[Seq[Target]], element: Element): ReferenceFloorplanAnnotation = ReferenceFloorplanAnnotation(targets, element.serialize) +object MemFloorplanAnnotation { + def apply(targets: Seq[Seq[Target]], element: Element): MemFloorplanAnnotation = MemFloorplanAnnotation(targets, element.serialize) } object NoReferenceFloorplanAnnotation { diff --git a/floorplan/src/main/scala/firrtl/Passes.scala b/floorplan/src/main/scala/firrtl/Passes.scala index fd0a599c..ac406468 100644 --- a/floorplan/src/main/scala/firrtl/Passes.scala +++ b/floorplan/src/main/scala/firrtl/Passes.scala @@ -2,10 +2,11 @@ package barstools.floorplan.firrtl import barstools.floorplan.{FloorplanSerialization, FloorplanElementRecord, FloorplanState} -import firrtl.{CircuitState, Namespace, Transform, AnnotationSeq, VerilogEmitter, DependencyAPIMigration} +import firrtl.{CircuitState, Transform, DependencyAPIMigration, VerilogEmitter} import firrtl.options.{Dependency, RegisteredTransform, ShellOption} -import firrtl.analyses.{InstanceKeyGraph} +import firrtl.analyses.{IRLookup} import firrtl.annotations.{InstanceTarget, ReferenceTarget, IsComponent} +import firrtl.annotations.TargetToken.{Instance, OfModule} // NOTE: If you rename/add this transform, don't forget to update META-INF // See the @note in the RegisteredTransform documentation @@ -40,17 +41,34 @@ class GenerateFloorplanIRPass extends Transform with RegisteredTransform with De } } - def newRecord(path: String, ref: Option[String], anno: FloorplanAnnotation) = FloorplanElementRecord(path, ref, FloorplanSerialization.deserialize(anno.fpir)) + def newRecord(path: String, ref: Option[String], anno: FloorplanAnnotation, ext: Option[String] = None) = + FloorplanElementRecord(path, ref, FloorplanSerialization.deserialize(anno.fpir), ext) val list = state.annotations.collect({ case x: NoReferenceFloorplanAnnotation => newRecord(getInstancePath(x.target), None, x) case x: InstanceFloorplanAnnotation if x.targets.flatten.length == 2 => - newRecord(getInstancePath(x.targets(0)(0).asInstanceOf[InstanceTarget]), - Some(getRelativePath(x.targets(0)(0).asInstanceOf[InstanceTarget], x.targets(1)(0).asInstanceOf[InstanceTarget])), x) - case x: ReferenceFloorplanAnnotation if x.targets.flatten.length == 2 => - newRecord(getInstancePath(x.targets(0)(0).asInstanceOf[InstanceTarget]), - Some(getRelativePath(x.targets(0)(0).asInstanceOf[InstanceTarget], x.targets(1)(0).asInstanceOf[ReferenceTarget])), x) + val rootTarget = x.targets(0)(0).asInstanceOf[InstanceTarget] + val instTarget = x.targets(1)(0).asInstanceOf[InstanceTarget] + newRecord(getInstancePath(rootTarget), Some(getRelativePath(rootTarget, instTarget)), x) + case x: MemFloorplanAnnotation if x.targets.flatten.length == 2 => + val rootTarget = x.targets(0)(0).asInstanceOf[InstanceTarget] + val refTarget = x.targets(1)(0).asInstanceOf[ReferenceTarget] + // Note: This assumes specific behavior from ReplSeqMem, namely that it replaces the Mem reference with + // a wrapper instance named ${ext} that instantiates an external bbox named ${ext}_ext + val mem = IRLookup(state.circuit).declaration(refTarget) match { + case m: firrtl.ir.DefInstance => m.module + case _ => throw new Exception("Something went wrong, Mems should become ExtModule instances") + } + val ext = mem+"_ext" + // TODO do we want to replace this in the output annotation file... ? + val newTarget = InstanceTarget( + circuit=refTarget.circuit, + module=refTarget.module, + instance=ext, + ofModule=ext, + path=refTarget.path :+ (Instance(refTarget.ref), OfModule(mem))) + newRecord(getInstancePath(rootTarget), Some(getRelativePath(rootTarget, newTarget)), x, Some(ext)) }) val filename = state.annotations.collectFirst({ From e324d83c40ee86ac7ef4424b689e55dbaae99a55 Mon Sep 17 00:00:00 2001 From: John Wright Date: Tue, 13 Jul 2021 16:25:21 -0700 Subject: [PATCH 28/73] Add text file that lists the macrocompiler mapping --- .../barstools/macros/MacroCompiler.scala | 62 ++++++++++++++++--- .../scala/barstools/macros/SynFlopsPass.scala | 4 +- 2 files changed, 57 insertions(+), 9 deletions(-) diff --git a/src/main/scala/barstools/macros/MacroCompiler.scala b/src/main/scala/barstools/macros/MacroCompiler.scala index 968e6b30..97b44e92 100644 --- a/src/main/scala/barstools/macros/MacroCompiler.scala +++ b/src/main/scala/barstools/macros/MacroCompiler.scala @@ -10,6 +10,7 @@ package barstools.macros import barstools.macros.Utils._ import firrtl.Utils.{one, zero, BoolType} import firrtl.annotations._ +import firrtl.annotations.TargetToken.{Instance, OfModule} import firrtl.ir._ import firrtl.options.Dependency import firrtl.stage.TransformManager.TransformDependency @@ -106,6 +107,7 @@ object MacroCompilerAnnotation { * @param memFormat Type of memory lib (Some("conf"), Some("mdf"), or None (defaults to mdf)) * @param lib Path to library lib or None if no libraries * @param hammerIR Path to HammerIR output or None (not generated in this case) + * @param instMap Path to instance map file output or None (not generated in this case) * @param costMetric Cost metric to use * @param mode Compiler mode (see CompilerMode) * @param forceCompile Set of memories to force compiling to lib regardless of the mode @@ -116,6 +118,7 @@ object MacroCompilerAnnotation { memFormat: Option[String], lib: Option[String], hammerIR: Option[String], + instMap: Option[String], costMetric: CostMetric, mode: CompilerMode, useCompiler: Boolean, @@ -137,6 +140,7 @@ class MacroCompilerPass( libs: Option[Seq[Macro]], compilers: Option[SRAMCompiler], hammerIR: Option[String], + instMap: Option[String], costMetric: CostMetric = CostMetric.default, mode: MacroCompilerAnnotation.CompilerMode = MacroCompilerAnnotation.Default) extends firrtl.passes.Pass { @@ -151,6 +155,10 @@ class MacroCompilerPass( }) } + // Set up some string buffers + val instMapBuffer = new ArrayBuffer[String]() + val hammerIRBuffer = new ArrayBuffer[String]() + /** Calculate bit pairs. * This is a list of submemories by width. * The tuples are (lsb, msb) inclusive. @@ -295,7 +303,7 @@ class MacroCompilerPass( bitPairs }.toSeq - def compile(mem: Macro, lib: Macro): Option[(Module, Macro)] = { + def compile(mem: Macro, lib: Macro): Option[(Module, Macro, Seq[(Instance, OfModule)])] = { assert( mem.sortedPorts.lengthCompare(lib.sortedPorts.length) == 0, "mem and lib should have an equal number of ports" @@ -313,9 +321,11 @@ class MacroCompilerPass( // Depth mapping val stmts = ArrayBuffer[Statement]() - val outputs = mutable.HashMap[String, ArrayBuffer[(Expression, Expression)]]() - val selects = mutable.HashMap[String, Expression]() - val selectRegs = mutable.HashMap[String, Expression]() + val outputs = HashMap[String, ArrayBuffer[(Expression, Expression)]]() + val selects = HashMap[String, Expression]() + val selectRegs = HashMap[String, Expression]() + // Store (instanceName, moduleName) tuples for use by floorplanning + val insts = ArrayBuffer[(Instance, OfModule)]() /* Palmer: If we've got a parallel memory then we've got to take the * address bits into account. */ if (mem.src.depth > lib.src.depth) { @@ -348,6 +358,8 @@ class MacroCompilerPass( val name = s"mem_${i}_$j" // Create the instance. stmts += WDefInstance(NoInfo, name, lib.src.name, lib.tpe) + // Store the instance and module tuple for use by floorplanning + insts += ((Instance(name), OfModule(lib.src.name))) // Connect extra ports of the lib. stmts ++= lib.extraPorts.map { case (portName, portValue) => Connect(NoInfo, WSubField(WRef(name), portName), portValue) @@ -597,11 +609,10 @@ class MacroCompilerPass( } } - Some((mem.module(Block(stmts.toSeq)), lib)) + Some((mem.module(Block(stmts.toSeq)), lib, insts)) } def run(c: Circuit): Circuit = { - var firstLib = true val modules = (mems, libs) match { case (Some(mems), Some(libs)) => // Try to compile each of the memories in mems. @@ -635,7 +646,11 @@ class MacroCompilerPass( // Try to compile mem against each lib in libs, keeping track of the // best compiled version, external lib used, and cost. +<<<<<<< HEAD:src/main/scala/barstools/macros/MacroCompiler.scala val (best, _) = fullLibs.foldLeft(None: Option[(Module, Macro)], Double.MaxValue) { +======= + val (best, cost) = (fullLibs.foldLeft(None: Option[(Module, Macro, Seq[(Instance, OfModule)])], Double.MaxValue)) { +>>>>>>> Add text file that lists the macrocompiler mapping:macros/src/main/scala/barstools/macros/MacroCompiler.scala case ((best, cost), lib) if mem.src.ports.size != lib.src.ports.size => /* Palmer: FIXME: This just assumes the Chisel and vendor ports are in the same * order, but I'm starting with what actually gets generated. */ @@ -667,6 +682,7 @@ class MacroCompilerPass( ) else modules +<<<<<<< HEAD:src/main/scala/barstools/macros/MacroCompiler.scala case Some((mod, bb)) => hammerIR match { case Some(f) => @@ -679,10 +695,33 @@ class MacroCompilerPass( case None => } modules.filterNot(m => m.name == mod.name || m.name == bb.blackbox.name) ++ Seq(mod, bb.blackbox) +======= + } + case Some((mod, bb, insts)) => + // Add hammerIR + hammerIRBuffer.append(bb.src.toJSON().toString()) + // Add insts + instMapBuffer.append(mod.name + " " + insts.map(x => x._1.value + ":" + x._2.value).mkString(" ")) + (modules.filterNot(m => m.name == mod.name || m.name == bb.blackbox.name)) ++ Seq(mod, bb.blackbox) +>>>>>>> Add text file that lists the macrocompiler mapping:macros/src/main/scala/barstools/macros/MacroCompiler.scala } } case _ => c.modules } + + // Write files if specified + hammerIR.foreach { f => + val writer = new FileWriter(new File(f)) + writer.write("[\n") + writer.write(hammerIRBuffer.mkString("\n,\n")) + writer.write("\n]\n") + writer.close() + } + instMap.foreach { f => + val writer = new FileWriter(new File(f)) + writer.write(instMapBuffer.mkString("\n")) + writer.close() + } c.copy(modules = modules) } } @@ -702,6 +741,7 @@ class MacroCompilerTransform extends Transform with DependencyAPIMigration { memFileFormat, libFile, hammerIR, + instMap, costMetric, mode, useCompiler, @@ -754,7 +794,7 @@ class MacroCompilerTransform extends Transform with DependencyAPIMigration { }.getOrElse(Seq.empty) val transforms = Seq( - new MacroCompilerPass(memCompile, libs, compilers, hammerIR, costMetric, mode), + new MacroCompilerPass(memCompile, libs, compilers, hammerIR, instMap, costMetric, mode), new SynFlopsPass( true, memSynflops ++ (if (mode == MacroCompilerAnnotation.CompileAndSynflops) { @@ -793,6 +833,7 @@ object MacroCompiler extends App { case object Verilog extends MacroParam case object Firrtl extends MacroParam case object HammerIR extends MacroParam + case object InstMap extends MacroParam case object CostFunc extends MacroParam case object Mode extends MacroParam case object UseCompiler extends MacroParam @@ -812,6 +853,7 @@ object MacroCompiler extends App { " -v, --verilog: Verilog output", " -f, --firrtl: FIRRTL output (optional)", " -hir, --hammer-ir: Hammer-IR output currently only needed for IP compilers", + " -im, --instance-map: Instance map output file", " -c, --cost-func: Cost function to use. Optional (default: \"default\")", " -cp, --cost-param: Cost function parameter. (Optional depending on the cost function.). e.g. -c ExternalMetric -cp path /path/to/my/cost/script", " --force-compile [mem]: Force the given memory to be compiled to target libs regardless of the mode", @@ -842,6 +884,8 @@ object MacroCompiler extends App { parseArgs(map + (Firrtl -> value), costMap, forcedMemories, tail) case ("-hir" | "--hammer-ir") :: value :: tail => parseArgs(map + (HammerIR -> value), costMap, forcedMemories, tail) + case ("-im" | "--instance-map") :: value :: tail => + parseArgs(map + (InstMap -> value), costMap, forcedMemories, tail) case ("-c" | "--cost-func") :: value :: tail => parseArgs(map + (CostFunc -> value), costMap, forcedMemories, tail) case ("-cp" | "--cost-param") :: value1 :: value2 :: tail => @@ -883,6 +927,7 @@ object MacroCompiler extends App { params.get(MacrosFormat), params.get(Library), params.get(HammerIR), + params.get(InstMap), CostMetric.getCostMetric(params.getOrElse(CostFunc, "default"), costParams), MacroCompilerAnnotation.stringToCompilerMode(params.getOrElse(Mode, "default")), params.contains(UseCompiler), @@ -909,6 +954,7 @@ object MacroCompiler extends App { ) ) +<<<<<<< HEAD:src/main/scala/barstools/macros/MacroCompiler.scala params.get(HammerIR) match { case Some(hammerIRFile: String) => val lines = FileUtils.getLines(hammerIRFile).toList @@ -919,6 +965,8 @@ object MacroCompiler extends App { hammerIRWriter.close() case None => } +======= +>>>>>>> Add text file that lists the macrocompiler mapping:macros/src/main/scala/barstools/macros/MacroCompiler.scala } else { // Warn user System.err.println("WARNING: Empty *.mems.conf file. No memories generated.") diff --git a/src/main/scala/barstools/macros/SynFlopsPass.scala b/src/main/scala/barstools/macros/SynFlopsPass.scala index 5dda0476..fa3b0057 100644 --- a/src/main/scala/barstools/macros/SynFlopsPass.scala +++ b/src/main/scala/barstools/macros/SynFlopsPass.scala @@ -47,8 +47,8 @@ class SynFlopsPass(synflops: Boolean, libs: Seq[Macro]) extends firrtl.passes.Pa ) ) ) - val mod_macro = new MacroCompilerPass(None, None, None, None).compile(lib, lib_macro) - val (real_mod, real_macro) = mod_macro.get + val mod_macro = (new MacroCompilerPass(None, None, None, None, None)).compile(lib, lib_macro) + val (real_mod, real_macro, insts) = mod_macro.get val mem = DefMemory( NoInfo, From 9da78d07fad5d34af9ba584d1f06cbb378443231 Mon Sep 17 00:00:00 2001 From: John Wright Date: Tue, 13 Jul 2021 16:58:08 -0700 Subject: [PATCH 29/73] Remove aoplib --- aoplib/src/main/scala/aoplib/AOP.scala | 34 --- .../src/main/scala/aoplib/LoggingAspect.scala | 158 ----------- .../aoplib/breakpoint/BreakpointAspect.scala | 246 ------------------ .../scala/aoplib/coverage/CoverAspect.scala | 109 -------- .../scala/aoplib/coverage/CoverGroup.scala | 85 ------ .../scala/aoplib/coverage/CoverPoint.scala | 90 ------- .../aoplib/coverage/CoverTransform.scala | 45 ---- .../aoplib/coverage/CoverageTracker.scala | 139 ---------- .../aoplib/custom/AnnotatingAspect.scala | 12 - .../aoplib/floorplan/FloorplanAspect.scala | 29 --- .../scala/aoplib/histogram/Histogram.scala | 91 ------- .../aoplib/histogram/HistogramAspect.scala | 89 ------- .../aoplib/histogram/HistogramSignal.scala | 32 --- .../aoplib/redundancy/RedundancyAspect.scala | 152 ----------- .../aoplib/redundancy/StuckFaultAspect.scala | 76 ------ 15 files changed, 1387 deletions(-) delete mode 100644 aoplib/src/main/scala/aoplib/AOP.scala delete mode 100644 aoplib/src/main/scala/aoplib/LoggingAspect.scala delete mode 100644 aoplib/src/main/scala/aoplib/breakpoint/BreakpointAspect.scala delete mode 100644 aoplib/src/main/scala/aoplib/coverage/CoverAspect.scala delete mode 100644 aoplib/src/main/scala/aoplib/coverage/CoverGroup.scala delete mode 100644 aoplib/src/main/scala/aoplib/coverage/CoverPoint.scala delete mode 100644 aoplib/src/main/scala/aoplib/coverage/CoverTransform.scala delete mode 100644 aoplib/src/main/scala/aoplib/coverage/CoverageTracker.scala delete mode 100644 aoplib/src/main/scala/aoplib/custom/AnnotatingAspect.scala delete mode 100644 aoplib/src/main/scala/aoplib/floorplan/FloorplanAspect.scala delete mode 100644 aoplib/src/main/scala/aoplib/histogram/Histogram.scala delete mode 100644 aoplib/src/main/scala/aoplib/histogram/HistogramAspect.scala delete mode 100644 aoplib/src/main/scala/aoplib/histogram/HistogramSignal.scala delete mode 100644 aoplib/src/main/scala/aoplib/redundancy/RedundancyAspect.scala delete mode 100644 aoplib/src/main/scala/aoplib/redundancy/StuckFaultAspect.scala diff --git a/aoplib/src/main/scala/aoplib/AOP.scala b/aoplib/src/main/scala/aoplib/AOP.scala deleted file mode 100644 index c27c68cd..00000000 --- a/aoplib/src/main/scala/aoplib/AOP.scala +++ /dev/null @@ -1,34 +0,0 @@ -package aoplib - -import firrtl.RenameMap -import firrtl.annotations.ReferenceTarget - -object Main extends App { - println("Hello world.") -} - -object AOP { - def doStuff = println("Doing stuff") -} - -object AnnotationHelpers { - def renameOne(rt: ReferenceTarget, renames: RenameMap): ReferenceTarget = { - renames.get(rt) match { - case Some(s@Seq(x: ReferenceTarget)) => x - case None => rt - case x => sys.error(s"Cannot update $this when $rt is renamed to $x.") - } - } - def renameMany(rts: Seq[ReferenceTarget], renames: RenameMap): Seq[ReferenceTarget] = { - rts.flatMap { t => - renames.get(t) match { - case Some(seq) => seq.map { - case x: ReferenceTarget => x - case x => sys.error(s"Cannot update $this when $t is renamed to $x.") - } - case None => Seq(t) - } - } - } - -} \ No newline at end of file diff --git a/aoplib/src/main/scala/aoplib/LoggingAspect.scala b/aoplib/src/main/scala/aoplib/LoggingAspect.scala deleted file mode 100644 index 84e304f6..00000000 --- a/aoplib/src/main/scala/aoplib/LoggingAspect.scala +++ /dev/null @@ -1,158 +0,0 @@ -package aoplib - -import chisel3.{Bool, Data} -import firrtl.annotations.{Annotation, ReferenceTarget, Target} -import firrtl.{AnnotationSeq, CircuitForm, CircuitState, HighForm, IRToWorkingIR, LowForm, RenameMap, ResolveAndCheck, ResolvedAnnotationPaths, Transform, UNKNOWNGENDER, WRef, WSubField, WSubIndex} -import firrtl.ir._ -import firrtl.Mappers._ - -import scala.collection.mutable -import scala.reflect.runtime.universe.TypeTag - - - -//abstract class LoggingInfo(message: String, signals: Seq[Data], clock: chisel3.Clock, enable: Option[Bool] = None) extends AspectInfo { - - -//case class FirrtlLoggingInfo(enable: ReferenceTarget, signals: Seq[ReferenceTarget], clock: ReferenceTarget, message: String) { - -//abstract class LoggingAspect[T <: RawModule](implicit tag: TypeTag[T]) extends Aspect[T, LoggingInfo] { -// -// def logSignals(info: LoggingInfo): Unit -// -// override def collectAspectInfo(dut: T): Seq[LoggingInfo] = logSignals(dut) -// -// override def resolveAspectInfo(aspectInfo: Seq[LoggingInfo]): AnnotationSeq = { -// val fLogSignals = aspectInfo.map(i => FirrtlLoggingInfo(i.message, i.signals.map(_.toTarget), i.clock.toTarget, i.enable.map(_.toTarget))) -// Seq(LoggingSignals(fLogSignals)) -// } -// -// override def transformClass: Class[_ <: Transform] = classOf[LoggingTransform] -//} - - -// FIRRTL STUFF BELOW - -/* - -case class FirrtlLoggingInfo(message: String, signals: Seq[ReferenceTarget], clock: ReferenceTarget, enable: Option[ReferenceTarget]) { - private val circuits = getTargets.map(t => t.circuit).toSet - private val modules = getTargets.map(t => t.module).toSet - assert(circuits.size == 1 && modules.size == 1, s"All logging signals must share the same circuits $circuits and modules $modules: $this") - val circuit: String = circuits.head - val module: String = modules.head - val enRef: Option[String] = enable.map(_.ref) - val sigRefs: Seq[String] = signals.map(_.ref) - val clkRef: String = clock.ref - def getTargets: Seq[ReferenceTarget] = clock +: (signals ++ enable) -} - - -case class LoggingSignals(infos: Seq[FirrtlLoggingInfo]) extends Annotation with AnnotationHelpers { - override def getTargets: Seq[ReferenceTarget] = infos.flatMap(i => i.getTargets) - override def update(renames: RenameMap): Seq[Annotation] = { - val newTargets = infos.map{ i => - FirrtlLoggingInfo(i.message, renameMany(i.signals, renames), renameOne(i.clock, renames), i.enable.map(e => renameOne(e, renames))) - } - Seq(LoggingSignals(newTargets)) - } -} - -class LoggingTransform extends Transform with ResolvedAnnotationPaths { - override def inputForm: CircuitForm = HighForm - override def outputForm: CircuitForm = HighForm - - - override val annotationClasses: Traversable[Class[_]] = Seq(classOf[LoggingSignals]) - - override protected def execute(state: CircuitState): CircuitState = { - val infos = state.annotations.collect{ case a: LoggingSignals => a}.flatMap( a => a.infos ) - println("Executing Logging Transform on the following:") - - new ResolveAndCheck().runTransform(state.copy(circuit = addLogs(state.circuit, infos))) - } - - def addLogs(circuit: Circuit, infos: Seq[FirrtlLoggingInfo]): Circuit = { - val moduleSignalMap = mutable.HashMap[String, mutable.ArrayBuffer[FirrtlLoggingInfo]]() - infos.foreach { i => - i.getTargets.foreach { t => - assert(/*t.component == Nil &&*/ t.path == Nil) - } - moduleSignalMap.getOrElseUpdate(i.module, mutable.ArrayBuffer[FirrtlLoggingInfo]()) += i - } - def toExp(r: ReferenceTarget): Expression = { - def toWExp(e: Expression): Expression = e map toWExp match { - case Reference(name, _) => WRef(name) - case SubField(expr, name, _) => WSubField(expr, name) - case SubIndex(expr, idx, _) => WSubIndex(expr, idx, UnknownType, UNKNOWNGENDER) - } - toWExp(r.toNamed.expr) - } - - def addModuleLogs(logStuff: Seq[FirrtlLoggingInfo])(stmt: Statement): Statement = { - val prints: Seq[Print] = logStuff.map { i => - val enable = if(i.enable.isDefined) toExp(i.enable.get) else UIntLiteral(1) - val x = Print(NoInfo, StringLit(i.message), i.signals.map(toExp), toExp(i.clock), enable) - println(x.serialize) - x - } - Block(stmt +: prints) - } - - circuit map { m: DefModule => - if(moduleSignalMap.contains(m.name)) m map addModuleLogs(moduleSignalMap(m.name)) else m - } - } -} -*/ - -/* -object AspectModule { - private[aspect] val dynamicContextVar = new DynamicVariable[Option[MonitorModule]](None) - private[aspect] def withAspect[S](m: MonitorModule)(thunk: => S): S = { - dynamicContextVar.withValue(Some(m))(thunk) - } - def getFirrtl(chiselIR: chisel3.internal.firrtl.Circuit): firrtl.ir.DefModule = { - // Build FIRRTL AST - val firrtlString = chisel3.internal.firrtl.Emitter.emit(chiselIR) - val firrtlIR = firrtl.Parser.parse(firrtlString) - firrtlIR.modules.head - } - def getAnnotations(chiselIR: Circuit, dut: AspectModule, connections: Seq[(Component, Component)], parent: BaseModule): Seq[ChiselAnnotation] = { - - val firrtlModule = getFirrtl(chiselIR) - - // Return Annotations - Seq( - AspectAnnotation( - connections, - parent.toNamed, - //new AddInstance(dut.instName, firrtlModule.name), - _ => (s: Statement) => Block(Seq(s, DefInstance(NoInfo, dut.instName, firrtlModule.name))), - Seq(firrtlModule)) - ) ++ chiselIR.annotations - } - - def apply[M<: MultiIOModule, T<:Data](instanceName: String, - parent: M, - f: Snippet[M, T] - ): (MonitorModule, Seq[ChiselAnnotation]) = { - val connections = (parent: M, dut: MonitorModule) => { - dut.cmrComponent.toMap.mapValues(_()) ++ Map((parent.clock.toNamed, dut.instComponent.ref("clock")), (parent.reset.toNamed, dut.instComponent.ref("reset"))) - } - apply(instanceName, parent, () => new MonitorModule(instanceName, parent).snip(f), connections) - } - - def apply[M<: MultiIOModule, S<:AspectModule with RawModule](instanceName: String, - parent: M, - aspect: () => S, - connections: (M, S) => Map[Component, Component] - ): (S, Seq[ChiselAnnotation]) = { - // Elaborate aspect - val (chiselIR, dut) = Driver.elaborateAndReturn(aspect) - val connects = connections(parent, dut) - (dut, getAnnotations(chiselIR, dut, connects.toSeq, parent)) - } -} -*/ - diff --git a/aoplib/src/main/scala/aoplib/breakpoint/BreakpointAspect.scala b/aoplib/src/main/scala/aoplib/breakpoint/BreakpointAspect.scala deleted file mode 100644 index 455e8464..00000000 --- a/aoplib/src/main/scala/aoplib/breakpoint/BreakpointAspect.scala +++ /dev/null @@ -1,246 +0,0 @@ -package aoplib.breakpoint - -import java.io.File - -import aoplib.AnnotationHelpers -import chisel3._ -import chisel3.aop.Aspect -import chisel3.aop.injecting.{InjectingAspect, InjectingTransform} -import chisel3.experimental.{ChiselAnnotation, annotate} -import firrtl.Mappers._ -import _root_.firrtl.annotations.{Annotation, ModuleTarget, ReferenceTarget} -import firrtl.ir._ -import firrtl.stage.RunFirrtlTransformAnnotation -import firrtl.{AnnotationSeq, CircuitForm, CircuitState, HighForm, LowForm, MidForm, PrimOps, RenameMap, ResolveAndCheck, ResolvedAnnotationPaths, Transform, WRef, WSubField, WSubIndex} - -import scala.collection.mutable -import scala.io.Source -import scala.reflect.runtime.universe.TypeTag - -case class BreakpointAspect[T <: RawModule, M <: RawModule](selectInstances: T => Iterable[M], - setBreakCondition: M => Bool, - selectSignals: M => Seq[Data], - selectClockReset: M => (Clock, Reset), - srcPath: String - ) - (implicit tTag: TypeTag[T]) extends Aspect[T] { - def align(strings: Seq[String]): Seq[String] = { - strings.zip(getSpaces(strings.map(_.length))).map { - case (str, spaces) => str + spaces - } - } - - def getSpaces(lengths: Seq[Int]): Seq[String] = { - val maxLength = lengths.foldLeft(0){ (maxLength, len) => len.max(maxLength) } - lengths.map { len => " " * (maxLength - len) } - } - - def nDigits(width: Int): Int = { - Math.ceil(Math.log10(Math.pow(2, width) + 1)).toInt - } - - override def toAnnotation(top: T): AnnotationSeq = { - InjectingAspect[T, M]( - selectInstances, - { m: M => - val breakWhen = setBreakCondition(m) - dontTouch(breakWhen) - val nextCycle = RegInit(false.B) - nextCycle := breakWhen - when(nextCycle) { - stop() - } - val signals = selectSignals(m).map(_.toTarget) - val (clock, reset) = selectClockReset(m) - annotate(new ChiselAnnotation { - /** Conversion to FIRRTL Annotation */ - override def toFirrtl: Annotation = { - val path = new File(".").getAbsolutePath() - val x = Breakpoint(breakWhen.toTarget, clock.toTarget, reset.toTarget, signals, path + "/" + srcPath) - x - } - }) - } - ).toAnnotation(top) ++ Seq(RunFirrtlTransformAnnotation(new InjectingTransform), RunFirrtlTransformAnnotation(new BreakpointTransform)) - } -} - -case class Breakpoint(whenBreak: ReferenceTarget, clock: ReferenceTarget, reset: ReferenceTarget, signals: Seq[ReferenceTarget], file: String) extends Annotation { - def module = whenBreak.module - def circuit = whenBreak.module - override def update(renames: RenameMap): Seq[Annotation] = { - val newSignals = AnnotationHelpers.renameMany(signals, renames) - val newWhenBreak = AnnotationHelpers.renameOne(whenBreak, renames) - Seq(Breakpoint(newWhenBreak, clock, reset, newSignals, file)) - } -} - - -class BreakpointTransform extends Transform with ResolvedAnnotationPaths { - override val annotationClasses: Traversable[Class[_]] = Seq(classOf[Breakpoint]) - // Import utility functions from companion object - import Breakpoint._ - override def inputForm: CircuitForm = MidForm - override def outputForm: CircuitForm = HighForm - - override def execute(state: CircuitState): CircuitState = { - val moduleToBP = state.annotations.collect { case b: Breakpoint => b }.groupBy(b => b.module) - - val newMods = state.circuit.modules.map { m => - moduleToBP.get(m.name) match { - case None => m - case Some(breaks) => - breaks.foldLeft(m){ (mod, break) => - assert(break.getTargets.forall(_.asInstanceOf[ReferenceTarget].component == Nil)) - assert(break.getTargets.map(_.asInstanceOf[ReferenceTarget]).forall(_.module == break.whenBreak.module), this.toString) - - breakModule(break.whenBreak.ref, break.signals.map(_.ref), break.clock.ref, break.reset.ref, break.file)(m) - } - } - } - - val newState = state.copy(circuit = state.circuit.copy(modules = newMods)) - new ResolveAndCheck().execute(newState) - } - - - def breakModule(when: String, signals: Seq[String], clock: String, reset: String, path: String)(m: DefModule): DefModule = { - val lines = Source.fromFile(path).getLines - val filename = getFileName(path) - val connects = firrtl.passes.memlib.AnalysisUtils.getConnects(m) - - // Maps line number to (declaration, column number) - val infoMap = mutable.HashMap[Int, mutable.ArrayBuffer[(IsDeclaration, Int)]]() - - // Populates infoMap - def updateInfoMap(dec: IsDeclaration)(info: Info): Unit = { - info match { - case FileInfo(StringLit(i)) => i match { - case infoRegex(file, lineNumber, columnNumber) => - val value = infoMap.getOrElseUpdate(lineNumber.toInt, mutable.ArrayBuffer.empty[(IsDeclaration, Int)]) - value += ((dec, columnNumber.toInt)) - } - case MultiInfo(infos) => infos.foreach(updateInfoMap(dec)) - case NoInfo => - } - } - - val signalsToRecord = signals.flatMap { s => findDeps(connects)(s) } ++ signals - - // Walks statements to populate infoMap - def findInfo(s: Statement): Unit = { - s match { - case i: IsDeclaration if signalsToRecord.contains(i.name) => updateInfoMap(i)(i.info) - case other => - } - s foreachStmt findInfo - } - m.ports.foreach(p => updateInfoMap(p)(p.info)) - m foreachStmt findInfo - - // Now infoMap is filled in - - val annotatedScala = mutable.ArrayBuffer[Print]() - val notReset = DoPrim(PrimOps.Not,Seq(WRef(reset)), Nil, UnknownType) - lines.zipWithIndex.foreach { case (line, prevIndex) => - val index = prevIndex + 1 - if(infoMap.contains(index)) { - val indent = selectIndent(line) - val decs = infoMap(index).filter{ case (dec, _) => getFileName(dec.info) == filename} - if(decs.size == 1) { - val (dec, _) = decs.head - // TODO: Will not work with instances or memories, WRef(dec.name) - annotatedScala += Print(NoInfo, StringLit("%c[1;31m"),Seq(UIntLiteral(27)), WRef(clock), notReset) - annotatedScala += Print(NoInfo, StringLit(line), Nil, WRef(clock), notReset) - annotatedScala += Print(NoInfo, StringLit("%c[0m"),Seq(UIntLiteral(27)), WRef(clock), notReset) - - annotatedScala += Print(NoInfo, StringLit("%c[1;34m"),Seq(UIntLiteral(27)), WRef(clock), notReset) - annotatedScala += Print(NoInfo, StringLit(dec.name + ":%d\n"), Seq(WRef(dec.name)), WRef(clock), notReset) - annotatedScala += Print(NoInfo, StringLit("%c[0m"),Seq(UIntLiteral(27)), WRef(clock), notReset) - } else { - annotatedScala += Print(NoInfo, StringLit("%c[1;31m"),Seq(UIntLiteral(27)), WRef(clock), notReset) - annotatedScala += Print(NoInfo, StringLit(line + "\n"), Nil, WRef(clock), notReset) - annotatedScala += Print(NoInfo, StringLit("%c[0m"),Seq(UIntLiteral(27)), WRef(clock), notReset) - if(decs.size == 0) { - println("HERE") - } - decs.foreach { case (dec, column) => - annotatedScala += Print(NoInfo, StringLit("%c[1;34m"),Seq(UIntLiteral(27)), WRef(clock), notReset) - annotatedScala += Print( - NoInfo, - StringLit((" " * 0.max(column - dec.name.length)) + dec.name + ":%d\n"), - Seq(WRef(dec.name)), // TODO: Will not work with instances or memories - WRef(clock), - notReset - ) - annotatedScala += Print(NoInfo, StringLit("%c[0m"),Seq(UIntLiteral(27)), WRef(clock), notReset) - } - - } - } else { - annotatedScala += Print(NoInfo, StringLit(line + "\n"), Nil, WRef(clock), notReset) - } - - - } - - m match { - case m: firrtl.ir.Module => - m.copy(body = Block(Seq(m.body, Conditionally(NoInfo, WRef(when), Block(annotatedScala), EmptyStmt)))) - case other => other - } - } - -} - -object Breakpoint { - /** Matches a Firrtl info, e.g. @[ALU.scala 40:19] */ - val infoRegex = """([^ ]+) ([0-9]+):([0-9]+)""".r - def getFileName(path: String): String = path.split("/").last - def getFileName(info: Info): String = { - info match { - case FileInfo(StringLit(i)) => i match { - case infoRegex(file, line, co) => file - case other => "" - } - case other => "" - } - } - - def selectIndent(line: String): String = { - val (spacing, _) = line.foldLeft(("", true)){ case ((soFar, collecting), c) => - if(collecting) { - c match { - case ' ' => (soFar + c, collecting) - case '\t' => (soFar + c, collecting) - case other => (soFar, false) - } - } else (soFar, false) - } - spacing - } - - // Finds all signals who eventually drive name - def findDeps(connects: firrtl.passes.memlib.AnalysisUtils.Connects)(name: String): collection.Set[String] = { - val deps = mutable.HashSet[String]() - def getDeps(e: Expression): Expression = { - e match { - case WRef(name, _, _, _) => deps += name - case x: WSubField => deps += x.serialize - case x: WSubIndex => deps += x.serialize - case other => other map getDeps - } - e - } - - connects.get(name) match { - case None => Set.empty[String] - case Some(e) => getDeps(e) - } - deps ++ deps.flatMap { - case d if !deps.contains(d) => findDeps(connects)(d) - case d => Set(d) - } - } - -} diff --git a/aoplib/src/main/scala/aoplib/coverage/CoverAspect.scala b/aoplib/src/main/scala/aoplib/coverage/CoverAspect.scala deleted file mode 100644 index c61a9e4c..00000000 --- a/aoplib/src/main/scala/aoplib/coverage/CoverAspect.scala +++ /dev/null @@ -1,109 +0,0 @@ -package aoplib.coverage - -import aoplib.histogram.{HistogramAspect, HistogramSignal} -import chisel3.{Bits, Bool, Data} -import chisel3.aop.injecting.{InjectingAspect, InjectingTransform} -import chisel3.aop.{Aspect, Select} -import chisel3.core.RawModule -import chisel3.experimental.{ChiselAnnotation, annotate} -import chisel3.util.experimental.BoringUtils -import firrtl.{AnnotationSeq, RenameMap} -import firrtl.annotations.{Annotation, IsMember} -import firrtl.options.Unserializable -import firrtl.passes.wiring.WiringTransform -import firrtl.stage.RunFirrtlTransformAnnotation - -import scala.collection.MapLike -import scala.reflect.runtime.universe.TypeTag - - -trait CoverageOption - -case object SimulatorDone extends CoverageOption - -case class CoverageOptions(options: Map[CoverageOption, Any]) { - def simDone[T <: RawModule](top: T): Bool = options(SimulatorDone).asInstanceOf[T => Bool](top) -} - -case class CoverAspect[T <: RawModule](buildCoverage: T => Seq[CoverGroup], - coverage: CoverageOptions) - (implicit tTag: TypeTag[T]) extends Aspect[T] { - - override def toAnnotation(top: T): AnnotationSeq = { - val coverGroups = buildCoverage(top) - val groupMap = coverGroups.groupBy { group => group.module } - val annoSeqs = Select.collectDeep(top) { - case x: RawModule if groupMap.contains(x) => - val groups = groupMap(x).toList - assert(groups.size == 1, "Currently only support one covergroup per module") - - val ia = InjectingAspect[T, RawModule]( - (t: T) => Seq(x), - { m: RawModule => - import chisel3._ - val signals = groups.flatMap { group => - group.points.map { case CoverPoint(labelx, sig, bins, options) => - val defaults = bins.collect { case b@Bin(_, Default) => b } - assert(defaults.size <= 1, s"Coverpoint $labelx on signal ${sig.signal.toTarget} can no more than one default bin.") - assert(defaults.isEmpty, s"Don't support Default bin yet! Sorry :(") - val binIntervals = bins.filter { _.category.isInstanceOf[BinRange] }.sortBy { - case Bin(_, BinRange(low, high)) => low - } - new HistogramSignal(sig.signal) { - override def intervals: Seq[(Option[String], Int, Int)] = binIntervals.map{ - case Bin(n, BinRange(low, high)) => (Some(n), low.toInt, high.toInt) - } - override def label: Option[String] = Some(labelx) - } - } - } - val done = Wire(Bool()) - done := DontCare - BoringUtils.bore(coverage.simDone(top), Seq(done)) - signals.foreach { s => - val tracker = Module(new CoverageTracker(s, s.signal.name, m.name)) - tracker.in := s.signal - tracker.recording := true.B - tracker.printCoverage := done - } - } - ).toAnnotation(top) - - // Create annotation to insert histogram execution after design execution - //val ia2 = InjectingAspect({dut: T => Seq(dut)}, { dut: T => setDone(selectSimDone(dut)) }).toAnnotation(dut) - - //ia ++ ia2 ++ Seq(RunFirrtlTransformAnnotation(new InjectingTransform), RunFirrtlTransformAnnotation(new WiringTransform)) - ia - } - val ret = annoSeqs.toList.foldRight(Seq[Annotation](RunFirrtlTransformAnnotation(new WiringTransform))){ case (sofar, next) => next ++ sofar } - ret - } -} - -object SignalTracker { - def apply(signal: Bits, selector: Seq[IsMember] => Option[IsMember] = ts => Some(ts.head)): SignalTracker = { - SignalTracker(signal, Nil, selector, expanded = false) - } -} - -case class SignalTracker(signal: Bits, targets: Seq[IsMember], finalSelection: Seq[IsMember] => Option[IsMember], expanded: Boolean) extends Annotation with Unserializable { - def singleUpdate(renames: RenameMap): SignalTracker = { - val renamed = update(renames) - assert(renamed.size == 1, "Signal Tracker should always be renamed to a single other SignalTracker") - renamed.head - } - - override def update(renames: RenameMap): Seq[SignalTracker] = { - val expandedTargets = if(!expanded) { - assert(targets.isEmpty, "If SignalTracker isn't expanded, its targets should be empty.") - Seq(signal.toTarget) - } else targets - val newMembers = expandedTargets.flatMap { m: IsMember => - renames.get(m) match { - case Some(seq) => seq - case None => Seq(m) - } - } - if(!expanded) Seq(this.copy(targets = newMembers, expanded=true)) else Seq(this.copy(targets = newMembers)) - } -} diff --git a/aoplib/src/main/scala/aoplib/coverage/CoverGroup.scala b/aoplib/src/main/scala/aoplib/coverage/CoverGroup.scala deleted file mode 100644 index cf2fc0e4..00000000 --- a/aoplib/src/main/scala/aoplib/coverage/CoverGroup.scala +++ /dev/null @@ -1,85 +0,0 @@ -package aoplib.coverage - -import aoplib.AnnotationHelpers -import chisel3.core.RawModule -import chisel3.aop.Aspect -import chisel3.aop.injecting.{InjectStatement, InjectingAspect, InjectingTransform} -import chisel3.{Clock, Reset} -import firrtl.annotations.Annotation -import firrtl.options.Unserializable -import firrtl.{AnnotationSeq, RenameMap} - -import scala.reflect.runtime.universe.TypeTag - -//object CoverGroup { -// def apply(label: String, -// module: RawModule, -// clock: Clock, -// reset: Reset, -// points: Seq[CoverPoint], -// options: GroupOptions = GroupOptions()): CoverGroup = { -// CoverGroup(label, module, clock, reset, points, options) -// } -//} - -case class CoverGroup (label: String, - module: RawModule, - clock: Clock, - reset: Reset, - points: Seq[CoverPoint], - options: GroupOptions = GroupOptions(), - ) extends Annotation with Unserializable { - override def update(renames: RenameMap): Seq[CoverGroup] = { - def updateTracker(t: SignalTracker): SignalTracker = { - val renamed = t.update(renames) - renamed.head - } - Seq(this.copy(points = points.map(_.update(renames)))) - } -} - -case class GroupOptions(weight: Int = 1) - -/* -weight=number, 1 - -If set at the covergroup syntactic level, it specifies the weight of this covergroup instance for computing the overalla - instance coverage of the simulation. -If set at the coverpoint (or cross) syntactic level, it specifies the weight of a coverpoint (or cross) for computing - the instance coverage of the enclosing covergroup. - -goal=number, 90 - -Specifies the target goal for a covergroup instance or for a coverpoint or a cross of an instance. - -name=string, unique name - -Specifies a name for the covergroup instance. - -comment=string - -A comment that appears with a covergroup instance or with a coverpoint or cross of the covergroup instance - -at_least=number, 1 - -Minimum number of times a bin needs to hit before it is declared as hit - -detect_overlap=boolean, 0 - -When true, a warning is issued if there is an overlap between the range list (or transition list) of two bins of a -coverpoint. - -auto_bin_max=number, 64 - -Maximum number of automatically created bins when no bins are explicitly defined for a coverpoint. - -cross_num_print_missing = number, 0 - -Number of missing (not covered) cross product bins that must be saved to the coverage database and printed in the -coverage report. - -per_instance=boolean, 0 - -Each instance contributes to the overall coverage information for the covergroup type. When true, coverage information -for this covergroup instance is tracked as well. - */ diff --git a/aoplib/src/main/scala/aoplib/coverage/CoverPoint.scala b/aoplib/src/main/scala/aoplib/coverage/CoverPoint.scala deleted file mode 100644 index 7ad6c4e0..00000000 --- a/aoplib/src/main/scala/aoplib/coverage/CoverPoint.scala +++ /dev/null @@ -1,90 +0,0 @@ -package aoplib.coverage - -import chisel3._ -import firrtl.RenameMap - -//trait CoverBase { val label: String } - - -object CoverPoint { - def apply(label: String, signal: Bits, bins: Seq[BaseBin], pointOptions: CoverOptions = CoverOptions()): CoverPoint = { - CoverPoint(label, SignalTracker(signal), bins, pointOptions) - } -} -case class CoverPoint private (label: String, - signal: SignalTracker, - bins: Seq[BaseBin], - pointOptions: CoverOptions) { - def update(renames: RenameMap): CoverPoint = { - this.copy(signal = signal.singleUpdate(renames)) - } - - /* - def generateChisel(clock: Clock, reset: Reset): Unit = { - val sig = signal.signal.asUInt() - withClockAndReset(clock, reset) { - val defaults = bins.collect { case b@Bin(_, Default) => b } - assert(defaults.size <= 1, s"Coverpoint $label on signal ${signal.signal.toTarget} can no more than one default bin.") - val inRanges = bins.map { - case Bin(label, BinRange(low, high)) => - val (counter, willWrap) = util.Counter(sig >= low.U & sig <= high.U, pointOptions.maxCount) - counter.suggestName(label) - when(willWrap) { - counter := counter - } - } - - } - - } - */ -} -case class CoverOptions(weights: Seq[Int] = Seq(1), maxCount: Int = 32) - -// TODO: I think you can get away without representing this directly and generating it programmatically -// case class CrossPoint(name: String, points: Seq[CoverPoint], bins: Seq[BaseBin]) extends CoverBase - -abstract class BaseBin { - val labelOption: Option[String] - val category: BinCategory -} - -// Explicit bin, bins based on category -case class Bin(label: String, category: BinCategory) extends BaseBin { - override val labelOption: Option[String] = Some(label) -} - -// Implicit bin, bins based on category -// Not user created -case class ImplicitBin(category: BinCategory) extends BaseBin { - override val labelOption: Option[String] = None -} - -// Ignores when bin matches (usually paired with ImplicitBin -case class IgnoreBin(label: String, category: BinCategory) extends BaseBin { - override val labelOption: Option[String] = Some(label) -} - - -trait BinCategory - -// Defaults to all non-specified categories -case object Default extends BinCategory - -// Low and High are inclusive -case class BinRange(low: BigInt, high: BigInt) extends BinCategory - -// A sequence of values that must be transitioned to, in order -// Wait on this... -//case class BinTransition(sequence: Seq[BinValue]) extends BinCategory - -// Unnecessary! -//trait BinValue - -// A value in a sequence that must match immediately -//case class BinConstant(value: BigInt) extends BinValue - -// A value that must be hit eventually, but not necessarily at this time -//case class BinEventually(value: BigInt) extends BinValue - - diff --git a/aoplib/src/main/scala/aoplib/coverage/CoverTransform.scala b/aoplib/src/main/scala/aoplib/coverage/CoverTransform.scala deleted file mode 100644 index 43c9069c..00000000 --- a/aoplib/src/main/scala/aoplib/coverage/CoverTransform.scala +++ /dev/null @@ -1,45 +0,0 @@ -/* -package aoplib.coverage - -import firrtl.annotations.{Annotation, ReferenceTarget, SingleTargetAnnotation} -import firrtl.{CircuitForm, CircuitState, LowForm, ResolvedAnnotationPaths, Transform} - -case class SimulationFinished(target: ReferenceTarget) extends SingleTargetAnnotation[ReferenceTarget] { - override def duplicate(n: ReferenceTarget): Annotation = SimulationFinished(n) -} - - -class CoverTransform extends Transform with ResolvedAnnotationPaths { - override def inputForm: CircuitForm = LowForm - override def outputForm: CircuitForm = LowForm - - override val annotationClasses: Traversable[Class[_]] = List(classOf[SimulationFinished]) - - override def execute(state: CircuitState): CircuitState = { - val simFinishedSeq = state.annotations.collect { - case c: SimulationFinished => c - } - - assert(simFinishedSeq.size == simFinishedSeq.toSet.size, - s"There can only be one finished simulation signal, found: ${simFinishedSeq.map(_.target.serialize)}") - - val done = simFinishedSeq.toSet.head.target - - val circuit = state.circuit - done match { - case ReferenceTarget(cir, mod, Nil, ref, Nil) => - assert(circuit.main == cir, s"Simulation Signal circuit does not match given circuit: $cir != ${circuit.main}") - assert(circuit.modules.map(_.name).contains(mod), s"Simulation Signal module $mod is not found in circuit $cir") - val module = circuit.modules.collectFirst{ - case m if m.name == mod => m - }.get - - - case other => sys.error(s"Malformed simulation finished signal target: $other") - } - - state - } - -} -*/ diff --git a/aoplib/src/main/scala/aoplib/coverage/CoverageTracker.scala b/aoplib/src/main/scala/aoplib/coverage/CoverageTracker.scala deleted file mode 100644 index 0129961f..00000000 --- a/aoplib/src/main/scala/aoplib/coverage/CoverageTracker.scala +++ /dev/null @@ -1,139 +0,0 @@ -package aoplib.coverage - -import aoplib.histogram.HistogramSignal -import chisel3._ - -/** Either records and bins the value of [in] every cycle, or cycles through all bins and prints the result - * - * @param hinfo contains signal (and its type) as well as other histogramming information - * @param name name of the instance of the parent module containing the signal - * @param module name of the parent module containing the signal - */ -class CoverageTracker(hinfo: HistogramSignal, name: String, module: String) extends MultiIOModule { - val in = IO(Input(chiselTypeOf(hinfo.signal))) - val recording = IO(Input(Bool())) - val printCoverage = IO(Input(Bool())) - - val inU = in.asUInt() - - val lows = VecInit(hinfo.intervals.map(_._2.U)) - val highs = VecInit(hinfo.intervals.map(_._3.U)) - - // Calculate in's address into histogram - val activeBinAddress = hinfo.intervals.zipWithIndex.foldLeft(0.U) { - case (addr: UInt, ((_, min: Int, max: Int), index: Int)) => Mux((inU >= min.U) & (inU < max.U), index.U, addr) - } - - // Instantiate coverage bins memory - val coverbins = Mem(math.pow(2, activeBinAddress.getWidth).toInt, chiselTypeOf(hinfo.maxCount.U)) - - // Records which bins have been written to (and which require initialization) - val hasWritten = RegInit(VecInit(Seq.fill(coverbins.length.toInt)(false.B))) - - // Represents the address of the current bin our input signal is falling into - val activeBinValue = Wire(chiselTypeOf(hinfo.maxCount.U)) - when(hasWritten(activeBinAddress)) { - activeBinValue := coverbins.read(activeBinAddress) - }.otherwise { - activeBinValue := 0.U - } - - // Then, do stuff - when(reset.asBool() === false.B) { - when(recording) { - val writeValue = (activeBinValue + 1.U).min(hinfo.maxCount.U) - coverbins.write(activeBinAddress, writeValue) - hasWritten(activeBinAddress) := true.B - } - - when(printCoverage) { - val (message, expsAll) = hinfo.intervals.zipWithIndex.foldLeft((s"Coverage of $name in module $module:\n", Seq.empty[Bits])) { - case ((str, exps), ((nameOpt, lo, hi), index)) => - /* - val start = if (hinfo.label.isDefined) { - s" ${hinfo.label.get}:" - } else s" $index:" - */ - val start = " " - val (strRest, expsRest) = if (nameOpt.isDefined) { - (start + s"Bin ${nameOpt.get} ($lo to $hi) -> %d\n", Seq(coverbins.read(index.U))) - } else { - (start + s"Bin $index ($lo to $hi) -> %d\n", Seq(coverbins.read(index.U))) - } - (str + strRest, exps ++ expsRest) - } - printf(message, expsAll:_*) - } - } -} - -/* - -covergroup address_cov (ref logic [7:0] address, - 22 input int low, int high) @ (posedge ce); - 23 ADDRESS : coverpoint address { - 24 bins low = {0,low}; - 25 bins med = {low,high}; - 26 } - 27 endgroup - 28 //================================================= - 29 // Instance of covergroup - 30 //================================================= - 31 address_cov acov_low = new(addr,0,10); - 32 address_cov acov_med = new(addr,11,20); - 33 address_cov acov_high = new(addr,21,30); - - =========================================================== - Group : coverage_covergroup.miff::address_cov - =========================================================== - SCORE WEIGHT GOAL - 100.00 1 100 - ----------------------------------------------------------- - Summary for Group coverage_covergroup.miff::address_cov - CATEGORY EXPECTED UNCOVERED COVERED PERCENT - Variables 2 0 2 100.00 - - Variables for Group coverage_covergroup.miff::address_cov - - VARIABLE EXPECTED UNCOVERED COVERED PERCENT GOAL WEIGHT - ADDRESS 2 0 2 100.00 100 1 - ----------------------------------------------------------- - Summary for Variable ADDRESS - - CATEGORY EXPECTED UNCOVERED COVERED PERCENT - User Defined Bins 2 0 2 100.00 - - User Defined Bins for ADDRESS - Bins - - NAME COUNT AT LEAST - med 2 1 - low 6 1 - */ - -/* -covergroup datac @ (negedge cif.cb.ce); - 83 data_in : coverpoint cif.cb.datai { - 84 bins low = {0,50}; - 85 bins med = {51,150}; - 86 bins high = {151,255}; - 87 } - 88 data_out : coverpoint cif.cb.datao { - 89 bins low = {0,50}; - 90 bins med = {51,150}; - 91 bins high = {151,255}; - 92 } - 93 read_write : coverpoint cif.cb.we { - 94 bins read = {0}; - 95 bins write = {1}; - 96 } - 97 endgroup - - VARIABLE EXPECTED UNCOVERED COVERED PERCENT GOAL WEIGHT - address 3 2 1 33.33 100 1 - - VARIABLE EXPECTED UNCOVERED COVERED PERCENT GOAL WEIGHT - data_in 3 2 1 33.33 100 1 - data_out 3 3 0 0.00 100 1 - read_write 2 0 2 100.00 100 1 - */ diff --git a/aoplib/src/main/scala/aoplib/custom/AnnotatingAspect.scala b/aoplib/src/main/scala/aoplib/custom/AnnotatingAspect.scala deleted file mode 100644 index b5fe9319..00000000 --- a/aoplib/src/main/scala/aoplib/custom/AnnotatingAspect.scala +++ /dev/null @@ -1,12 +0,0 @@ -package aoplib.custom - -import chisel3.aop.Aspect -import chisel3.core.RawModule -import firrtl.{AnnotationSeq, Transform} - -import scala.reflect.runtime.universe.TypeTag - -case class AnnotatingAspect[T <: RawModule](annotateSignals: T => AnnotationSeq) - (implicit tTag: TypeTag[T]) extends Aspect[T] { - override def toAnnotation(top: T): AnnotationSeq = annotateSignals(top) -} diff --git a/aoplib/src/main/scala/aoplib/floorplan/FloorplanAspect.scala b/aoplib/src/main/scala/aoplib/floorplan/FloorplanAspect.scala deleted file mode 100644 index 5af7c684..00000000 --- a/aoplib/src/main/scala/aoplib/floorplan/FloorplanAspect.scala +++ /dev/null @@ -1,29 +0,0 @@ -package aoplib.floorplan -import chisel3._ -import chisel3.aop.Aspect -import chisel3.core.Reset -import chisel3.experimental.{RunFirrtlTransform} -import firrtl.annotations._ -import firrtl.options.Unserializable -import firrtl.stage.RunFirrtlTransformAnnotation -import firrtl.{AnnotationSeq, CircuitForm, CircuitState, LowForm, MidForm, RenameMap, Transform} - -import scala.reflect.runtime.universe.TypeTag - -case class MemberTracker(name: String, targets: Seq[IsMember], finalSelection: Seq[IsMember] => Option[IsMember]) extends Annotation with Unserializable { - override def update(renames: RenameMap): Seq[Annotation] = { - val newMembers = targets.flatMap { m: IsMember => - renames.get(m) match { - case Some(seq) => seq - case None => Seq(m) - } - } - Seq(this.copy(targets = newMembers)) - } -} - -case class FloorplanAspect[T <: RawModule](buildFloorplan: T => AnnotationSeq)(implicit tTag: TypeTag[T]) extends Aspect[T] { - override def toAnnotation(top: T): AnnotationSeq = { - buildFloorplan(top) :+ RunFirrtlTransformAnnotation(new barstools.floorplan.firrtl.GenerateFloorplanIRPass()) - } -} diff --git a/aoplib/src/main/scala/aoplib/histogram/Histogram.scala b/aoplib/src/main/scala/aoplib/histogram/Histogram.scala deleted file mode 100644 index cbd325ba..00000000 --- a/aoplib/src/main/scala/aoplib/histogram/Histogram.scala +++ /dev/null @@ -1,91 +0,0 @@ -package aoplib.histogram - -import chisel3._ - -/** Either records and bins the value of [in] every cycle, or cycles through all bins and prints the result - * - * @param hinfo contains signal (and its type) as well as other histogramming information - * @param name name of the instance of the parent module containing the signal - * @param module name of the parent module containing the signal - */ -class Histogram(hinfo: HistogramSignal, name: String, module: String) extends MultiIOModule { - val in = IO(Input(chiselTypeOf(hinfo.signal))) - val setHistogramming = IO(Input(Bool())) - val setReadingOut = IO(Input(Bool())) - val doneReading = IO(Output(Bool())) - val allDone = IO(Input(Bool())) - doneReading := false.B - - assert((setHistogramming === false.B) | (setReadingOut === false.B), "Inputs setHistogramming and setReadingOut cannot both be true") - - val inU = in.asUInt() - // Calculate in's address into histogram - val inAddr = hinfo.intervals.zipWithIndex.foldLeft(0.U) { - case (addr: UInt, ((_, min: Int, max: Int), index: Int)) => Mux((inU >= min.U) & (inU < max.U), index.U, addr) - } - val lows = VecInit(hinfo.intervals.map(_._2.U)) - val highs = VecInit(hinfo.intervals.map(_._3.U)) - - // Instantiate histogram mem, actual read address (could change if not in histogramming mode), and readPort - val histMem = Mem(math.pow(2, inAddr.getWidth).toInt, chiselTypeOf(hinfo.maxCount.U)) - val readAddress = Wire(chiselTypeOf(inAddr.asUInt)) - readAddress := 0.U - val readPort = histMem.read(readAddress) - - // Calculate Read Value of input - val readValue = Wire(chiselTypeOf(readPort)) - val hasWritten = RegInit(VecInit(Seq.fill(histMem.length.toInt)(false.B))) - when(hasWritten(readAddress)) { - readValue := readPort - }.otherwise { - readValue := 0.U - } - - // Update histogram, or read out histogram - // First, remember previous state of setReadingOut - val pastSetReadingOut = RegNext(setReadingOut) - - // Then, do stuff - when(reset.asBool() === false.B) { - val readOutCounter = RegInit(chiselTypeOf(readAddress), 0.U) - when(setHistogramming) { - readAddress := inAddr.asUInt - val writeValue = (readPort + 1.U).min(hinfo.maxCount.U) - histMem.write(inAddr.asUInt(), writeValue) - hasWritten(inAddr.asUInt()) := true.B - } - - when(setReadingOut) { - readAddress := readOutCounter - when(pastSetReadingOut === false.B) { - // First cycle we are reading out the histogram - printf(s"Histogram for signal $name in module $module.\n") - } - val prevAddress = RegNext(readAddress) - when(readAddress < (lows.size - 1).U & (readAddress >= prevAddress)) { - readOutCounter := readOutCounter + 1.U - //printf(s"Bin %d (%d until %d) -> %d\n", readAddress, ticks(readAddress), ticks(readAddress +& 1.U), readPort) - }.otherwise { - doneReading := true.B - } - hinfo.intervals.zipWithIndex.foreach { case ((nameOpt, lo, hi), index) => - when(readAddress === index.U) { - val start = if(hinfo.label.isDefined) { - s"Histogram ${hinfo.label.get}" - } else s"Histogram $index" - - if(nameOpt.isDefined) { - printf(start + s" Bin ${nameOpt.get} (%d until %d) -> %d\n", lows(readAddress), highs(readAddress +& 1.U), readPort) - } else { - printf(start + s" Bin %d (%d until %d) -> %d\n", readAddress, lows(readAddress), highs(readAddress +& 1.U), readPort) - } - } - } - } - - when(allDone) { - doneReading := true.B - } - - } -} diff --git a/aoplib/src/main/scala/aoplib/histogram/HistogramAspect.scala b/aoplib/src/main/scala/aoplib/histogram/HistogramAspect.scala deleted file mode 100644 index 8e83991e..00000000 --- a/aoplib/src/main/scala/aoplib/histogram/HistogramAspect.scala +++ /dev/null @@ -1,89 +0,0 @@ -package aoplib.histogram - -import chisel3._ -import chisel3.aop._ -import chisel3.aop.injecting.{InjectingAspect, InjectingTransform} -import chisel3.experimental.{ChiselAnnotation, annotate} -import chisel3.util.experimental.BoringUtils -import firrtl.annotations.Annotation -import firrtl.passes.wiring.WiringTransform -import firrtl.stage.RunFirrtlTransformAnnotation -import firrtl.{AnnotationSeq, Transform} - -import scala.reflect.runtime.universe.TypeTag - -/** Create histograms of signal values during execution, and print the bin values at the end of execution - * - * @param selectRoots Given top-level module, pick the instances of a module to apply the aspect (root module) - * @param selectSignals Pick signals from a module to histogram - * @param selectDesignDone From the top-level design, select a signal which, when true, the design has finished execution - * @param selectSimDone From the top-level design, select an assignable signal which, when true, the simulation has finished execution - * @param tTag Needed to prevent type-erasure of the top-level module type - * @param mTag Needed to prevent type-erasure of the selected modules' type - * @tparam T Type of top-level module - * @tparam M Type of root module (join point) - */ -case class HistogramAspect[T <: RawModule, M <: RawModule](selectRoots: T => Seq[M], - selectSignals: M => Seq[HistogramSignal], - selectDesignDone: T => Bool, - selectSimDone: T => Bool) - (implicit tTag: TypeTag[T], mTag: TypeTag[M]) extends Aspect[T] { - private final def markDone(d: Data): Unit = { - annotate(new ChiselAnnotation { - override def toFirrtl: Annotation = firrtl.passes.wiring.SourceAnnotation(d.toTarget, "histogramDone") - }) - } - - private final def setDone(d: Data): Unit = { - annotate(new ChiselAnnotation { - override def toFirrtl: Annotation = firrtl.passes.wiring.SinkAnnotation(d.toNamed, "histogramDone") - }) - } - - final def toAnnotation(dut: T): AnnotationSeq = { - // Create annotation to insert histogram into module - val ia = InjectingAspect[T, M]( - selectRoots, - { m: M => - val signals = selectSignals(m) - val done = Wire(Bool()) - done := DontCare - BoringUtils.bore(selectDesignDone(dut), Seq(done)) - val histograms = signals.map { s => - val histogram = Module(new Histogram(s, s.signal.name, m.name)) - histogram.in := s.signal - histogram.setHistogramming := true.B - histogram.setReadingOut := false.B - histogram.allDone := false.B - histogram - } - - val allDone = WireInit(false.B) - - when(done) { - histograms.foreach { h => - h.setHistogramming := false.B - } - allDone := histograms.foldLeft(1.U) { (readOut, h) => - h.setReadingOut := readOut - val regNext = RegNext(h.doneReading) - when(regNext) { - h.setReadingOut := false.B - h.allDone := true.B - } - - readOut & h.doneReading - } - } - - dontTouch(allDone) - markDone(allDone) - } - ).toAnnotation(dut) - - // Create annotation to insert histogram execution after design execution - val ia2 = InjectingAspect({dut: T => Seq(dut)}, { dut: T => setDone(selectSimDone(dut)) }).toAnnotation(dut) - - ia ++ ia2 ++ Seq(RunFirrtlTransformAnnotation(new InjectingTransform), RunFirrtlTransformAnnotation(new WiringTransform)) - } -} diff --git a/aoplib/src/main/scala/aoplib/histogram/HistogramSignal.scala b/aoplib/src/main/scala/aoplib/histogram/HistogramSignal.scala deleted file mode 100644 index 5c7fa644..00000000 --- a/aoplib/src/main/scala/aoplib/histogram/HistogramSignal.scala +++ /dev/null @@ -1,32 +0,0 @@ -package aoplib.histogram - -import chisel3._ - -/** Specifies signal whose values will be histogrammed after execution - * - * Can overwrite functions to customize the histogram behavior - * - * @param signal Signal to histogram - */ -class HistogramSignal(val signal: Bits) { - def maxCount = 100 - def minValue: Int = signal match { - case _: UInt => 0 - case s: SInt => Math.pow(2, s.getWidth - 1).toInt - } - /* Until max is the smallest illegal value, or the max legal value plus 1 */ - def untilMax: Int = signal match { - case u: UInt => Math.pow(2, u.getWidth).toInt - case s: SInt => Math.pow(2, s.getWidth - 1).toInt - } - def nBins: Int = untilMax - minValue - def ticks: Seq[Int] = { - val binInterval = (untilMax - minValue) / nBins - assert(binInterval * nBins + minValue == untilMax, - s"nBins ${nBins} must divide evenly into the range from ${minValue} until ${untilMax}") - val range = Range(minValue, untilMax + 1, binInterval) - range.toList - } - def intervals: Seq[(Option[String], Int, Int)] = ticks.zip(ticks.tail).map { case (lo, hi) => (None, lo, hi) } - def label: Option[String] = None -} diff --git a/aoplib/src/main/scala/aoplib/redundancy/RedundancyAspect.scala b/aoplib/src/main/scala/aoplib/redundancy/RedundancyAspect.scala deleted file mode 100644 index d2be55c2..00000000 --- a/aoplib/src/main/scala/aoplib/redundancy/RedundancyAspect.scala +++ /dev/null @@ -1,152 +0,0 @@ -package aoplib.redundancy - -import aoplib.AnnotationHelpers -import chisel3.Data -import chisel3.core.RawModule -import chisel3.aop.Aspect -import chisel3.experimental.{RunFirrtlTransform} -import firrtl.{AnnotationSeq, CircuitForm, CircuitState, HighForm, LowFirrtlOptimization, LowForm, SourceFlow, MidForm, Namespace, RenameMap, ResolveAndCheck, ResolvedAnnotationPaths, Transform, WRef} -import firrtl.annotations.{Annotation, ReferenceTarget} -import firrtl.stage.RunFirrtlTransformAnnotation - -import scala.collection.mutable -import scala.reflect.runtime.universe.TypeTag - -/** Adds triple redundancy to the selected registers - * - * @param selectRegisters Select registers in a module to give triple redundancy - * @param tTag Needed to prevent type-erasure of the top-level module type - * @tparam T Type of top-level module - */ -case class RedundancyAspect[T <: RawModule](selectRegisters: T => Iterable[Data]) - (implicit tTag: TypeTag[T]) extends Aspect[T] { - override def toAnnotation(top: T): AnnotationSeq = { - Seq(RedundancyRegisters(selectRegisters(top).map(_.toTarget).toList), RunFirrtlTransformAnnotation(new RedundancyTransform)) - } -} - -case class RedundancyRegisters(regs: Seq[ReferenceTarget]) extends Annotation { - override def update(renames: RenameMap): Seq[Annotation] = { - Seq(RedundancyRegisters(AnnotationHelpers.renameMany(regs, renames))) - } -} - -class RedundancyTransform extends Transform with ResolvedAnnotationPaths { - import firrtl.ir._ - import firrtl.Mappers._ - override def inputForm: CircuitForm = MidForm - override def outputForm: CircuitForm = HighForm - - override val annotationClasses: Traversable[Class[_]] = Seq(classOf[RedundancyRegisters]) - - case class RegInfo(red0: String, red1: String, output: String, tpe: Option[Type] = None) - - override def execute(state: CircuitState): CircuitState = { - val redundantRegs = state.annotations.flatMap { - case r: RedundancyRegisters => r.regs - case other => Nil - } - - val regModuleMap = mutable.HashMap[String, Set[String]]() - redundantRegs.foreach { rt => - assert(rt.path == Nil && rt.component == Nil, - s"Cannot have a register reference target with a component or a path: $rt") - regModuleMap(rt.module) = regModuleMap.getOrElse(rt.module, Set.empty[String]) + rt.ref - } - - val newModules = state.circuit.modules.map { - case m: Module if regModuleMap.contains(m.name) => - val regMap = mutable.HashMap[String, RegInfo]() - val ns = Namespace(m) - regModuleMap(m.name).foreach { r => - val red0 = ns.newName(r) - val red1 = ns.newName(r) - regMap(r) = RegInfo(red0, red1, ns.newName(r)) - } - val ret = m map tripleRegs(regMap)// map addMuxing(regMap) - //println(ret.serialize) - ret - case other => other - } - - val newState = state.copy( - circuit = state.circuit.copy(modules = newModules), - annotations = state.annotations.filterNot(_.isInstanceOf[RedundancyRegisters]) - ) - - new ResolveAndCheck().execute(newState) - } - - private def tripleRegs(regMap: mutable.Map[String, RegInfo])(s: Statement): Statement = { - s match { - case d: DefRegister if regMap.contains(d.name) => - regMap(d.name) = regMap(d.name).copy(tpe = Some(d.tpe)) - val info = regMap(d.name) - val wire = DefWire(NoInfo, info.output, info.tpe.get) - - // Hack to change reset value to name of this register - regMap(d.name) = info.copy(output = info.red0) - val reg0 = d.copy(name = info.red0) map changeOutput(regMap) - regMap(d.name) = info.copy(output = info.red1) - val reg1 = d.copy(name = info.red1) map changeOutput(regMap) - //Change back - regMap(d.name) = info.copy(output = info.output) - - val r0 = WRef(d) - val r1 = WRef(info.red0) - val r2 = WRef(info.red1) - val o = WRef(info.output) - val assignment = Seq( - Conditionally(NoInfo, cond(r0, r1, r2), Connect(NoInfo, o, r0), - Conditionally(NoInfo, cond(r1, r2, r0), Connect(NoInfo, o, r1), - Conditionally(NoInfo, cond(r2, r0, r1), Connect(NoInfo, o, r2), Connect(NoInfo, o, r0)) - ) - ) - ) - Block(Seq(d, reg0, reg1, wire) ++ assignment) - case con@Connect(_, w@WRef(reg, _, _, _), expr) if regMap.contains(reg) => - val c = con.copy(expr = changeOutput(regMap)(expr)) - Block(Seq( - c, - c.copy(loc = w.copy(name = regMap(reg).red0)), - c.copy(loc = w.copy(name = regMap(reg).red1)) - )) - case other => other map changeOutput(regMap) map tripleRegs(regMap) - } - } - - private def changeOutput(regMap: collection.Map[String, RegInfo])(expr: Expression): Expression = expr match { - case w@WRef(reg, _, _, SourceFlow) if regMap.contains(reg) => w.copy(name = regMap(reg).output) - case e => e map changeOutput(regMap) - } - - def cond(agree1: WRef, agree2: WRef, disagree: WRef): Expression = { - import firrtl.PrimOps._ - val u1 = UIntType(IntWidth(1)) - val a = DoPrim(Eq, Seq(agree1, agree2), Nil, u1) - val d = DoPrim(Neq, Seq(agree1, disagree), Nil, u1) - DoPrim(And, Seq(a, d), Nil, u1) - } - - private def addMuxing(regMap: collection.Map[String, RegInfo])(s: Statement): Statement = { - val wireDefs = regMap.map { case (reg, info) => - DefWire(NoInfo, info.output, info.tpe.get) - } - val assignments = regMap.map { case (reg, info) => - val r0 = WRef(reg) - val r1 = WRef(info.red0) - val r2 = WRef(info.red1) - val o = WRef(info.output) - val assignment = Block(Seq( - Conditionally(NoInfo, cond(r0, r1, r2), Connect(NoInfo, o, r0), - Conditionally(NoInfo, cond(r1, r2, r0), Connect(NoInfo, o, r1), - Conditionally(NoInfo, cond(r2, r0, r1), Connect(NoInfo, o, r2), Connect(NoInfo, o, r0)) - ) - ) - )) - assignment - } - - Block(Seq(Block(wireDefs.toList), s, Block(assignments.toList))) - } -} diff --git a/aoplib/src/main/scala/aoplib/redundancy/StuckFaultAspect.scala b/aoplib/src/main/scala/aoplib/redundancy/StuckFaultAspect.scala deleted file mode 100644 index 03e69ab6..00000000 --- a/aoplib/src/main/scala/aoplib/redundancy/StuckFaultAspect.scala +++ /dev/null @@ -1,76 +0,0 @@ -package aoplib.redundancy - -import aoplib.AnnotationHelpers -import chisel3.Data -import chisel3.aop.Aspect -import chisel3.core.{RawModule} -import chisel3.experimental.{RunFirrtlTransform} -import firrtl.{AnnotationSeq, CircuitForm, CircuitState, MidForm, RenameMap, ResolvedAnnotationPaths, Transform, WRef} -import firrtl.annotations.{Annotation, ReferenceTarget} - -import scala.collection.mutable -import scala.reflect.runtime.universe.TypeTag - -case class StuckFaultAspect[DUT <: RawModule, M <: RawModule](selectSignals: DUT => Seq[Data]) - (implicit dutTag: TypeTag[DUT]) extends Aspect[DUT] { - override def toAnnotation(dut: DUT): AnnotationSeq = { - Seq(FaultyRegisters(selectSignals(dut).map(_.toTarget))) - } -} - - -case class FaultyRegisters(regs: Seq[ReferenceTarget]) extends Annotation with RunFirrtlTransform { - override def update(renames: RenameMap): Seq[Annotation] = { - Seq(FaultyRegisters(AnnotationHelpers.renameMany(regs, renames))) - } - override def transformClass: Class[_ <: Transform] = classOf[StuckFaultTransform] - override def toFirrtl = this -} - -class StuckFaultTransform extends Transform with ResolvedAnnotationPaths { - import firrtl.ir._ - import firrtl.Mappers._ - override def inputForm: CircuitForm = MidForm - override def outputForm: CircuitForm = MidForm - - override val annotationClasses: Traversable[Class[_]] = Seq(classOf[FaultyRegisters]) - - override def execute(state: CircuitState): CircuitState = { - val faultyRegs = state.annotations.flatMap { - case r: FaultyRegisters => r.regs - case other => Nil - } - - val regModuleMap = mutable.HashMap[String, Set[String]]() - faultyRegs.foreach { rt => - assert(rt.path == Nil && rt.component == Nil, - s"Cannot have a register reference target with a component or a path: $rt") - regModuleMap(rt.module) = regModuleMap.getOrElse(rt.module, Set.empty[String]) + rt.ref - } - - val newModules = state.circuit.modules.map { - case m: Module if regModuleMap.contains(m.name) => - val ret = m map makeFaulty(regModuleMap(m.name))// map addMuxing(regMap) - ret - case other => other - } - - val newState = state.copy( - circuit = state.circuit.copy(modules = newModules), - annotations = state.annotations.filterNot(_.isInstanceOf[FaultyRegisters]) - ) - - newState - } - - private def makeFaulty(regs: Set[String])(s: Statement): Statement = { - s match { - case con@Connect(_, w@WRef(reg, tpe, _, _), expr) if regs.contains(reg) => - tpe match { - case _: UIntType => con.copy(expr = UIntLiteral(0)) - case _: SIntType => con.copy(expr = SIntLiteral(0)) - } - case other => other map makeFaulty(regs) - } - } -} From 128ef308e8cc9c0ca94fba6ac06c5ac4fa5708a1 Mon Sep 17 00:00:00 2001 From: John Wright Date: Wed, 14 Jul 2021 01:35:37 -0700 Subject: [PATCH 30/73] Make SRAM replacement work --- floorplan/src/main/scala/FloorplanState.scala | 37 +++++++++- floorplan/src/main/scala/IR.scala | 9 +++ .../scala/compiler/FloorplanCompiler.scala | 25 +++++-- .../main/scala/compiler/FloorplanTree.scala | 2 +- .../src/main/scala/compiler/MemInstMap.scala | 29 ++++++++ .../src/main/scala/compiler/Passes.scala | 70 +++++++++++++------ .../scala/compiler/SidebandAnnotation.scala | 4 ++ .../barstools/macros/MacroCompiler.scala | 2 +- 8 files changed, 149 insertions(+), 29 deletions(-) create mode 100644 floorplan/src/main/scala/compiler/MemInstMap.scala create mode 100644 floorplan/src/main/scala/compiler/SidebandAnnotation.scala diff --git a/floorplan/src/main/scala/FloorplanState.scala b/floorplan/src/main/scala/FloorplanState.scala index 769f0731..422ea763 100644 --- a/floorplan/src/main/scala/FloorplanState.scala +++ b/floorplan/src/main/scala/FloorplanState.scala @@ -1,9 +1,11 @@ // See LICENSE for license details package barstools.floorplan +import java.io.{File, FileWriter} + final case class FloorplanElementRecord(root: String, inst: Option[String], element: Element, memExt: Option[String] = None) -final case class FloorplanState(elements: Seq[FloorplanElementRecord], level: Int) +final case class FloorplanState(records: Seq[FloorplanElementRecord], level: Int) object FloorplanState { @@ -14,4 +16,37 @@ object FloorplanState { def deserialize(str: String): FloorplanState = FloorplanSerialization.deserializeState(str) + def fromFile(file: String): FloorplanState = { + val source = scala.io.Source.fromFile(file) + val fpState = deserialize(source.getLines.mkString("\n")) + source.close() + fpState + } + + def toFile(file: String, fmt: OutputFormat, state: FloorplanState) { + val writer = new FileWriter(new File(file)) + fmt match { + case OutputFormat.HammerIR => writer.write(FloorplanState.toHammerIR(state)) + case OutputFormat.FloorplanIR => writer.write(FloorplanState.serialize(state)) + } + writer.close() + } + + def fromFiles(files: Seq[String]): FloorplanState = { + assert(files.length == 1, "FIXME combine multiple states into one") + fromFile(files(0)) + } + + def toHammerIR(state: FloorplanState): String = { + assert(state.level == 0, "Can only convert level 0 FloorplanState") + ??? + } } + +sealed trait OutputFormat + +object OutputFormat { + case object HammerIR extends OutputFormat + case object FloorplanIR extends OutputFormat +} + diff --git a/floorplan/src/main/scala/IR.scala b/floorplan/src/main/scala/IR.scala index b99b6f5a..d5c7619f 100644 --- a/floorplan/src/main/scala/IR.scala +++ b/floorplan/src/main/scala/IR.scala @@ -46,6 +46,10 @@ object IRLevel { def max = 2 } +sealed abstract class AbstractRectPrimitive extends Primitive { + final def level = 2 +} + sealed abstract class ConstrainedRectPrimitive extends Primitive with ConstrainedRectLike { final def level = 1 } @@ -120,6 +124,11 @@ private[floorplan] final case class MemElementArray( def level = 2 } +private[floorplan] final case class AbstractMacro ( + name: String +) extends AbstractRectPrimitive + + private[floorplan] final case class ConcreteMacro ( name: String, width: LengthUnit, diff --git a/floorplan/src/main/scala/compiler/FloorplanCompiler.scala b/floorplan/src/main/scala/compiler/FloorplanCompiler.scala index 84800b51..1bcba277 100644 --- a/floorplan/src/main/scala/compiler/FloorplanCompiler.scala +++ b/floorplan/src/main/scala/compiler/FloorplanCompiler.scala @@ -4,8 +4,10 @@ package barstools.floorplan.compiler import barstools.floorplan._ case class FloorplanOptions( - inputFloorplanFile: String = "", - outputFloorplanFile: String = "" + outFile: String = "", + outFmt: OutputFormat = OutputFormat.HammerIR, + inFiles: Seq[String] = Seq(), + memInstMapFiles: Seq[String] = Seq() ) object FloorplanCompiler extends App { @@ -15,17 +17,32 @@ object FloorplanCompiler extends App { opt[String]('i', "input-file"). required(). valueName(""). - action((x, c) => c.copy(inputFloorplanFile = x)). + action((x, c) => c.copy(inFiles = c.inFiles :+ x)). text("input file name") opt[String]('o', "output-file"). required(). valueName(""). - action((x, c) => c.copy(outputFloorplanFile = x)). + action((x, c) => c.copy(outFile = x)). text("output file name") + opt[String]('m', "mem-inst-file"). + required(). + valueName(""). + action((x, c) => c.copy(memInstMapFiles = c.memInstMapFiles :+ x)). + text("file containing the memory instance map") + + opt[Unit]('f', "output-fpir"). + action((x, c) => c.copy(outFmt = OutputFormat.FloorplanIR)). + text("emit floorplanIR") + }).parse(args, FloorplanOptions()).getOrElse { throw new Exception("Error parsing options!") } + // TODO make FloorplanPasses customizable + val fpStateIn = FloorplanState.fromFiles(opts.inFiles) + val fpStateOut = FloorplanPasses(opts).foldLeft(fpStateIn) { (state, pass) => pass.execute(state) } + FloorplanState.toFile(opts.outFile, opts.outFmt, fpStateOut) + } diff --git a/floorplan/src/main/scala/compiler/FloorplanTree.scala b/floorplan/src/main/scala/compiler/FloorplanTree.scala index 07f3ac56..2b2e54a1 100644 --- a/floorplan/src/main/scala/compiler/FloorplanTree.scala +++ b/floorplan/src/main/scala/compiler/FloorplanTree.scala @@ -69,7 +69,7 @@ class FloorplanTree(state: FloorplanState) { (path, tag) } - state.elements.foreach { record => + state.records.foreach { record => val (path, tag) = parsePathAndTag(record.root) root.addElement(path, tag, record.element) } diff --git a/floorplan/src/main/scala/compiler/MemInstMap.scala b/floorplan/src/main/scala/compiler/MemInstMap.scala new file mode 100644 index 00000000..949c070b --- /dev/null +++ b/floorplan/src/main/scala/compiler/MemInstMap.scala @@ -0,0 +1,29 @@ +// See LICENSE for license details +package barstools.floorplan.compiler + +import scala.collection.Map +import scala.collection.mutable.HashMap +import java.io.File +import scala.io.Source + +import firrtl.annotations.TargetToken.{Instance, OfModule} + +object MemInstMap { + + def fromFiles(files: Seq[String]): Map[OfModule, Seq[(Instance, OfModule)]] = { + val hashMap = new HashMap[OfModule, Seq[(Instance, OfModule)]]() + files.foreach { f => + Source.fromFile(new File(f)).getLines.foreach { line: String => + val listRaw = line.split(" ") + val module = OfModule(listRaw(0)) + hashMap += module -> listRaw.toSeq.drop(1).map { x => + val s = x.split(":") + (Instance(s(0)), OfModule(s(1))) + } + } + } + hashMap.toMap + } + +} + diff --git a/floorplan/src/main/scala/compiler/Passes.scala b/floorplan/src/main/scala/compiler/Passes.scala index f4d4f6e4..8cc2104c 100644 --- a/floorplan/src/main/scala/compiler/Passes.scala +++ b/floorplan/src/main/scala/compiler/Passes.scala @@ -1,40 +1,66 @@ // See LICENSE for license details package barstools.floorplan.compiler -import barstools.floorplan.FloorplanState +import firrtl.annotations.TargetToken.{Instance, OfModule} + +import barstools.floorplan._ +import scala.collection.Map + +object FloorplanPasses { + + // TODO make this customizable + def apply(opts: FloorplanOptions): Seq[Pass] = { + val instMap = MemInstMap.fromFiles(opts.memInstMapFiles) + val sbAnnos = Seq() // TODO + Seq( + new SidebandAnnotationPass(sbAnnos), + new TransformMemsPass(instMap), + new TopDownPropagationPass, + new BottomUpPropagationPass, + new ResolveConstraintsPass + ) + } +} abstract class Pass { - def execute(state: FloorplanState): FloorplanState - } -class SidebandAnnotationPass { - - def execute(state: FloorplanState): FloorplanState = ??? - +class SidebandAnnotationPass(annos: Seq[SidebandAnnotation]) extends Pass { + def execute(state: FloorplanState): FloorplanState = state // TODO } -class MacroLayoutsPass { - - def execute(state: FloorplanState): FloorplanState = ??? - +class TopDownPropagationPass extends Pass { + def execute(state: FloorplanState): FloorplanState = state // TODO } +class BottomUpPropagationPass extends Pass { + def execute(state: FloorplanState): FloorplanState = state // TODO +} -class BottomUpPropagationPass { +class ResolveConstraintsPass extends Pass { + def execute(state: FloorplanState): FloorplanState = state // TODO +} +class TransformMemsPass(instMap: Map[OfModule, Seq[(Instance, OfModule)]]) extends Pass { def execute(state: FloorplanState): FloorplanState = { - val tree = FloorplanTree(state) - ??? + val newRecords = state.records.map({ record => + record.element match { + case e: MemElement => + // TODO fail gracefully if key does not exist + instMap(OfModule(record.memExt.get)).map { case (inst: Instance, ofMod: OfModule) => + val element = AbstractMacro(ofMod.value) + FloorplanElementRecord( + root = record.root, + inst = record.inst.map(_ + "/" + inst.value), + element = element, + memExt = None + ) + } + case _ => Seq(record) + } + }).flatten + state.copy(records = newRecords) } - -} - -class TopDownPropagationPass { - - def execute(state: FloorplanState): FloorplanState = ??? - } - diff --git a/floorplan/src/main/scala/compiler/SidebandAnnotation.scala b/floorplan/src/main/scala/compiler/SidebandAnnotation.scala new file mode 100644 index 00000000..50fc9d3b --- /dev/null +++ b/floorplan/src/main/scala/compiler/SidebandAnnotation.scala @@ -0,0 +1,4 @@ +// See LICENSE for license details +package barstools.floorplan.compiler + +case class SidebandAnnotation(name: String) // TODO diff --git a/src/main/scala/barstools/macros/MacroCompiler.scala b/src/main/scala/barstools/macros/MacroCompiler.scala index 97b44e92..a681623c 100644 --- a/src/main/scala/barstools/macros/MacroCompiler.scala +++ b/src/main/scala/barstools/macros/MacroCompiler.scala @@ -140,7 +140,7 @@ class MacroCompilerPass( libs: Option[Seq[Macro]], compilers: Option[SRAMCompiler], hammerIR: Option[String], - instMap: Option[String], + instMap: Option[String], costMetric: CostMetric = CostMetric.default, mode: MacroCompilerAnnotation.CompilerMode = MacroCompilerAnnotation.Default) extends firrtl.passes.Pass { From 5913abdd4f3cf1c49f7ef204fbc079e3d103697a Mon Sep 17 00:00:00 2001 From: John Wright Date: Wed, 14 Jul 2021 12:43:40 -0700 Subject: [PATCH 31/73] Move floorplan --- .../src/main/scala/{ => barstools/floorplan}/Constraints.scala | 0 .../src/main/scala/{ => barstools/floorplan}/FloorplanState.scala | 0 floorplan/src/main/scala/{ => barstools/floorplan}/IR.scala | 0 .../src/main/scala/{ => barstools/floorplan}/Serialization.scala | 0 .../src/main/scala/{ => barstools/floorplan}/chisel/API.scala | 0 .../scala/{ => barstools/floorplan}/chisel/FloorplanAspect.scala | 0 .../src/main/scala/{ => barstools/floorplan}/chisel/package.scala | 0 .../{ => barstools/floorplan}/compiler/FloorplanCompiler.scala | 0 .../scala/{ => barstools/floorplan}/compiler/FloorplanTree.scala | 0 .../scala/{ => barstools/floorplan}/compiler/MemInstMap.scala | 0 .../main/scala/{ => barstools/floorplan}/compiler/Passes.scala | 0 .../{ => barstools/floorplan}/compiler/SidebandAnnotation.scala | 0 .../main/scala/{ => barstools/floorplan}/firrtl/Annotations.scala | 0 .../src/main/scala/{ => barstools/floorplan}/firrtl/Passes.scala | 0 14 files changed, 0 insertions(+), 0 deletions(-) rename floorplan/src/main/scala/{ => barstools/floorplan}/Constraints.scala (100%) rename floorplan/src/main/scala/{ => barstools/floorplan}/FloorplanState.scala (100%) rename floorplan/src/main/scala/{ => barstools/floorplan}/IR.scala (100%) rename floorplan/src/main/scala/{ => barstools/floorplan}/Serialization.scala (100%) rename floorplan/src/main/scala/{ => barstools/floorplan}/chisel/API.scala (100%) rename floorplan/src/main/scala/{ => barstools/floorplan}/chisel/FloorplanAspect.scala (100%) rename floorplan/src/main/scala/{ => barstools/floorplan}/chisel/package.scala (100%) rename floorplan/src/main/scala/{ => barstools/floorplan}/compiler/FloorplanCompiler.scala (100%) rename floorplan/src/main/scala/{ => barstools/floorplan}/compiler/FloorplanTree.scala (100%) rename floorplan/src/main/scala/{ => barstools/floorplan}/compiler/MemInstMap.scala (100%) rename floorplan/src/main/scala/{ => barstools/floorplan}/compiler/Passes.scala (100%) rename floorplan/src/main/scala/{ => barstools/floorplan}/compiler/SidebandAnnotation.scala (100%) rename floorplan/src/main/scala/{ => barstools/floorplan}/firrtl/Annotations.scala (100%) rename floorplan/src/main/scala/{ => barstools/floorplan}/firrtl/Passes.scala (100%) diff --git a/floorplan/src/main/scala/Constraints.scala b/floorplan/src/main/scala/barstools/floorplan/Constraints.scala similarity index 100% rename from floorplan/src/main/scala/Constraints.scala rename to floorplan/src/main/scala/barstools/floorplan/Constraints.scala diff --git a/floorplan/src/main/scala/FloorplanState.scala b/floorplan/src/main/scala/barstools/floorplan/FloorplanState.scala similarity index 100% rename from floorplan/src/main/scala/FloorplanState.scala rename to floorplan/src/main/scala/barstools/floorplan/FloorplanState.scala diff --git a/floorplan/src/main/scala/IR.scala b/floorplan/src/main/scala/barstools/floorplan/IR.scala similarity index 100% rename from floorplan/src/main/scala/IR.scala rename to floorplan/src/main/scala/barstools/floorplan/IR.scala diff --git a/floorplan/src/main/scala/Serialization.scala b/floorplan/src/main/scala/barstools/floorplan/Serialization.scala similarity index 100% rename from floorplan/src/main/scala/Serialization.scala rename to floorplan/src/main/scala/barstools/floorplan/Serialization.scala diff --git a/floorplan/src/main/scala/chisel/API.scala b/floorplan/src/main/scala/barstools/floorplan/chisel/API.scala similarity index 100% rename from floorplan/src/main/scala/chisel/API.scala rename to floorplan/src/main/scala/barstools/floorplan/chisel/API.scala diff --git a/floorplan/src/main/scala/chisel/FloorplanAspect.scala b/floorplan/src/main/scala/barstools/floorplan/chisel/FloorplanAspect.scala similarity index 100% rename from floorplan/src/main/scala/chisel/FloorplanAspect.scala rename to floorplan/src/main/scala/barstools/floorplan/chisel/FloorplanAspect.scala diff --git a/floorplan/src/main/scala/chisel/package.scala b/floorplan/src/main/scala/barstools/floorplan/chisel/package.scala similarity index 100% rename from floorplan/src/main/scala/chisel/package.scala rename to floorplan/src/main/scala/barstools/floorplan/chisel/package.scala diff --git a/floorplan/src/main/scala/compiler/FloorplanCompiler.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanCompiler.scala similarity index 100% rename from floorplan/src/main/scala/compiler/FloorplanCompiler.scala rename to floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanCompiler.scala diff --git a/floorplan/src/main/scala/compiler/FloorplanTree.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanTree.scala similarity index 100% rename from floorplan/src/main/scala/compiler/FloorplanTree.scala rename to floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanTree.scala diff --git a/floorplan/src/main/scala/compiler/MemInstMap.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/MemInstMap.scala similarity index 100% rename from floorplan/src/main/scala/compiler/MemInstMap.scala rename to floorplan/src/main/scala/barstools/floorplan/compiler/MemInstMap.scala diff --git a/floorplan/src/main/scala/compiler/Passes.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/Passes.scala similarity index 100% rename from floorplan/src/main/scala/compiler/Passes.scala rename to floorplan/src/main/scala/barstools/floorplan/compiler/Passes.scala diff --git a/floorplan/src/main/scala/compiler/SidebandAnnotation.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/SidebandAnnotation.scala similarity index 100% rename from floorplan/src/main/scala/compiler/SidebandAnnotation.scala rename to floorplan/src/main/scala/barstools/floorplan/compiler/SidebandAnnotation.scala diff --git a/floorplan/src/main/scala/firrtl/Annotations.scala b/floorplan/src/main/scala/barstools/floorplan/firrtl/Annotations.scala similarity index 100% rename from floorplan/src/main/scala/firrtl/Annotations.scala rename to floorplan/src/main/scala/barstools/floorplan/firrtl/Annotations.scala diff --git a/floorplan/src/main/scala/firrtl/Passes.scala b/floorplan/src/main/scala/barstools/floorplan/firrtl/Passes.scala similarity index 100% rename from floorplan/src/main/scala/firrtl/Passes.scala rename to floorplan/src/main/scala/barstools/floorplan/firrtl/Passes.scala From 46d7db2812381021d90a380e4a16e8fa4d889107 Mon Sep 17 00:00:00 2001 From: John Wright Date: Fri, 16 Jul 2021 02:45:19 -0700 Subject: [PATCH 32/73] Get top-level annotations working --- .../main/scala/barstools/floorplan/IR.scala | 31 +++- .../barstools/floorplan/chisel/API.scala | 166 ++++++++++++------ .../floorplan/firrtl/Annotations.scala | 12 +- .../barstools/floorplan/firrtl/Passes.scala | 53 ++++-- .../transforms/GenerateTopAndHarness.scala | 1 + .../FloorplanReParentTransform.scala | 58 ++++++ 6 files changed, 236 insertions(+), 85 deletions(-) create mode 100644 tapeout/src/main/scala/barstools/tapeout/transforms/FloorplanReParentTransform.scala diff --git a/floorplan/src/main/scala/barstools/floorplan/IR.scala b/floorplan/src/main/scala/barstools/floorplan/IR.scala index d5c7619f..9cb46e1e 100644 --- a/floorplan/src/main/scala/barstools/floorplan/IR.scala +++ b/floorplan/src/main/scala/barstools/floorplan/IR.scala @@ -112,23 +112,42 @@ private[floorplan] final case class WeightedGrid( // Reference to a MemElement private[floorplan] final case class MemElement( name: String -) extends Primitive { - def level = 2 -} +) extends AbstractRectPrimitive // Container for MemElements private[floorplan] final case class MemElementArray( name: String, - elements: Seq[Option[String]] -) extends Group { + elements: Seq[Option[String]], + width: Constraint[LengthUnit] = Unconstrained[LengthUnit], + height: Constraint[LengthUnit] = Unconstrained[LengthUnit], + area: Constraint[AreaUnit] = Unconstrained[AreaUnit], + aspectRatio: Constraint[Rational] = Unconstrained[Rational] +) extends Group with ConstrainedRectLike { def level = 2 } +// Container for MemElements that have been converted to Macros +// This may be unnecessary, but the purpose of this is to let the floorplan +// tool treat memory macros differently than generic macros, since they +// are more commonly arrayed +private[floorplan] final case class MemMacroArray( + name: String, + elements: Seq[Option[String]], + width: Constraint[LengthUnit] = Unconstrained[LengthUnit], + height: Constraint[LengthUnit] = Unconstrained[LengthUnit], + area: Constraint[AreaUnit] = Unconstrained[AreaUnit], + aspectRatio: Constraint[Rational] = Unconstrained[Rational] +) extends Group with ConstrainedRectLike { + def level = 1 +} + +// Reference to a macro blackbox with unknown dimensions +// Do not use for SyncReadMem objects; use MemElement instead private[floorplan] final case class AbstractMacro ( name: String ) extends AbstractRectPrimitive - +// Reference to a macro blackbox that has known dimensions private[floorplan] final case class ConcreteMacro ( name: String, width: LengthUnit, diff --git a/floorplan/src/main/scala/barstools/floorplan/chisel/API.scala b/floorplan/src/main/scala/barstools/floorplan/chisel/API.scala index b1ec1858..c6a339aa 100644 --- a/floorplan/src/main/scala/barstools/floorplan/chisel/API.scala +++ b/floorplan/src/main/scala/barstools/floorplan/chisel/API.scala @@ -1,9 +1,9 @@ // See LICENSE for license details package barstools.floorplan.chisel -import chisel3.{RawModule} +import chisel3.{RawModule, MemBase, Data} -import firrtl.annotations.{ReferenceTarget, InstanceTarget, Target, Annotation} +import firrtl.annotations.{ReferenceTarget, InstanceTarget, ModuleTarget, Target, Annotation} import barstools.floorplan._ import barstools.floorplan.firrtl.{FloorplanAnnotation, MemFloorplanAnnotation, InstanceFloorplanAnnotation, NoReferenceFloorplanAnnotation} @@ -11,12 +11,18 @@ import scala.collection.mutable.{ArraySeq, ArrayBuffer, HashMap, Set, HashSet} final case class ChiselFloorplanException(message: String) extends Exception(message: String) -final class ChiselFloorplanContext private[chisel] (val root: InstanceTarget, topElement: ChiselElement) { +final class ChiselFloorplanContext private[chisel] (val root: Target, topElement: ChiselElement) { private[chisel] val elementBuf = new ArrayBuffer[ChiselElement]() elementBuf.append(topElement) + private def addElement[T <: ChiselElement](e: T): T = { + FloorplanDatabase.register(root, e) + elementBuf.append(e) + e + } + def elements: Seq[ChiselElement] = elementBuf.toSeq def createRect[T <: RawModule](module: T, @@ -25,13 +31,11 @@ final class ChiselFloorplanContext private[chisel] (val root: InstanceTarget, to area: Constraint[AreaUnit] = Unconstrained[AreaUnit], aspectRatio: Constraint[Rational] = Unconstrained[Rational], hardBoundary: Boolean = true - ): ChiselElement = { - val inst: InstanceTarget = module.toAbsoluteTarget.asInstanceOf[InstanceTarget] - val name = FloorplanDatabase.getUnusedName(root, inst.instance) + ): ChiselLogicRect = { + val inst: Target = module.toAbsoluteTarget + val name = FloorplanDatabase.getUnusedName(root, inst) val elt = new ChiselLogicRect(root, name, inst, width, height, area, aspectRatio, hardBoundary) - FloorplanDatabase.register(root, elt) - elementBuf.append(elt) - elt + addElement(elt) } def createDummy( @@ -40,37 +44,32 @@ final class ChiselFloorplanContext private[chisel] (val root: InstanceTarget, to height: Constraint[LengthUnit] = Unconstrained[LengthUnit], area: Constraint[AreaUnit] = Unconstrained[AreaUnit], aspectRatio: Constraint[Rational] = Unconstrained[Rational] - ): ChiselElement = { + ): ChiselDummyRect = { val nameStr = FloorplanDatabase.getUnusedName(root, name) val elt = new ChiselDummyRect(root, nameStr, width, height, area, aspectRatio) - FloorplanDatabase.register(root, elt) - elementBuf.append(elt) - elt + addElement(elt) + } + + def createMemArray(name: Option[String] = None): ChiselMemArray = { + val nameStr = FloorplanDatabase.getUnusedName(root, name) + val elt = new ChiselMemArray(root, nameStr, this) + addElement(elt) } - def addMem[T <: chisel3.Data]( - mem: chisel3.MemBase[T] - ): ChiselElement = { + def addHier[T <: RawModule](module: T): ChiselHierarchicalBarrier = { + val inst: Target = module.toAbsoluteTarget + val name = FloorplanDatabase.getUnusedName(root, inst) + val elt = new ChiselHierarchicalBarrier(root, name, inst) + addElement(elt) + } + + private[chisel] def addMem[T <: Data](mem: MemBase[T]): ChiselMem = { val ref = mem.toAbsoluteTarget - val name = FloorplanDatabase.getUnusedName(root, ref.ref) + val name = FloorplanDatabase.getUnusedName(root, ref) val elt = new ChiselMem(root, name, ref) - FloorplanDatabase.register(root, elt) - elementBuf.append(elt) - elt + addElement(elt) } -/* - def grid[T <: RawModule](module: T, - name: String, - x: Int = 1, - y: Int = 1, - packed: Boolean = false - ) = { - val elt = new ChiselWeightedGrid(module, name, x, y, packed) - FloorplanDatabase.register(module, elt) - elt - } - */ } object Floorplan { @@ -82,8 +81,13 @@ object Floorplan { aspectRatio: Constraint[Rational] = Unconstrained[Rational], hardBoundary: Boolean = true ) = { - val root: InstanceTarget = module.toAbsoluteTarget.asInstanceOf[InstanceTarget] - val name = FloorplanDatabase.getUnusedName(root, root.ofModule) + val root: Target = module.toAbsoluteTarget + val modName = root match { + case r: InstanceTarget => r.ofModule + case r: ModuleTarget => r.module + case _ => ??? + } + val name = FloorplanDatabase.getUnusedName(root, modName) val elt = new ChiselLogicRect(root, name, root, width, height, area, aspectRatio, hardBoundary) FloorplanDatabase.register(root, elt) new ChiselFloorplanContext(root, elt) @@ -93,25 +97,27 @@ object Floorplan { private[chisel] object FloorplanDatabase { - private val nameMap = new HashMap[InstanceTarget, Set[String]]() + private val nameMap = new HashMap[Target, Set[String]]() private val elements = new HashSet[ChiselElement]() - private def getSet(root: InstanceTarget) = nameMap.getOrElseUpdate(root, new HashSet[String]) + private def getSet(root: Target) = nameMap.getOrElseUpdate(root, new HashSet[String]) // TODO I'm not sure this is necessary anymore - def register(root: InstanceTarget, element: ChiselElement): Unit = { + def register(root: Target, element: ChiselElement): Unit = { val name = element.name val set = getSet(root) if (set.contains(name)) { - throw new ChiselFloorplanException(s"Duplicate floorplan element registration ${name} for InstanceTarget "+root.asPath.toList.map(_._1.value).mkString(".")+"!") + throw new ChiselFloorplanException(s"Duplicate floorplan element registration ${name} for Target "+root.toString+"!") } elements.add(element) set.add(name) } - def getUnusedName(root: InstanceTarget, suggestion: Option[String]): String = getUnusedName(root, suggestion.getOrElse("unnamed")) + def getUnusedName(root: Target): String = getUnusedName(root, None) + + def getUnusedName(root: Target, suggestion: Option[String]): String = getUnusedName(root, suggestion.getOrElse("unnamed")) - def getUnusedName(root: InstanceTarget, suggestion: String): String = { + def getUnusedName(root: Target, suggestion: String): String = { val set = getSet(root) var id = 0 // This is slow and bad, but hopefully rare @@ -119,6 +125,16 @@ private[chisel] object FloorplanDatabase { suggestion + s"_${id}" } + def getUnusedName(root: Target, inst: Target): String = { + val instName = inst match { + case t: InstanceTarget => t.instance + case t: ModuleTarget => t.module + case t: ReferenceTarget => t.ref + case _ => ??? + } + getUnusedName(root, instName) + } + } object FloorplanUnits { @@ -167,71 +183,107 @@ object FloorplanUnits { } -sealed abstract class ChiselElement(val root: InstanceTarget, val name: String) { +sealed abstract class ChiselElement(val root: Target, val name: String) { protected def generateElement(): Element private[chisel] def getFloorplanAnnotations(): Seq[FloorplanAnnotation] def getAnnotations(): Seq[Annotation] = getFloorplanAnnotations() } -sealed abstract class ChiselDummyElement(root: InstanceTarget, name: String) extends ChiselElement(root, name) { +sealed abstract class ChiselDummyElement(root: Target, name: String) extends ChiselElement(root, name) { private[chisel] def getFloorplanAnnotations() = Seq(NoReferenceFloorplanAnnotation(root, generateElement())) } -sealed abstract class ChiselInstanceElement(root: InstanceTarget, name: String, val instance: InstanceTarget) extends ChiselElement(root, name) { +sealed abstract class ChiselInstanceElement(root: Target, name: String, val instance: Target) extends ChiselElement(root, name) { private[chisel] def getFloorplanAnnotations() = Seq(InstanceFloorplanAnnotation(Seq(Seq(root), Seq(instance)), generateElement())) } -sealed abstract class ChiselMemElement(root: InstanceTarget, name: String, val reference: ReferenceTarget) extends ChiselElement(root, name) { +sealed abstract class ChiselMemElement(root: Target, name: String, val reference: ReferenceTarget) extends ChiselElement(root, name) { private[chisel] def getFloorplanAnnotations() = Seq(MemFloorplanAnnotation(Seq(Seq(root), Seq(reference)), generateElement())) } -sealed abstract class ChiselGroupElement(root: InstanceTarget, name: String) extends ChiselElement(root, name) { - protected def elements: Seq[Option[ChiselElement]] +sealed abstract class ChiselGroupElement(root: Target, name: String, val context: ChiselFloorplanContext) extends ChiselElement(root, name) { + protected def initialSize: Int + private val elements = Seq.fill(initialSize)(Option.empty[ChiselElement]).toBuffer + private var isCommitted = false + + protected def generateGroupElement(names: Seq[Option[String]]): Group + + protected def generateElement(): Group = { + isCommitted = true + generateGroupElement(elements.map(_.map(_.name))) + } + + private[chisel] def placeElementAt(e: ChiselElement, idx: Int) { + assert(!isCommitted, "Cannot add elements after committing") + // This is only supported in scala 2.13 + //elements.padToInPlace(idx+1, None) + for (i <- elements.length until (idx+1)) elements += None + elements(idx) = Some(e) + } + + private[chisel] def addElement(e: ChiselElement) { + assert(!isCommitted, "Cannot add elements after committing") + elements += Some(e) + } + private[chisel] def getFloorplanAnnotations() = Seq(NoReferenceFloorplanAnnotation(root, generateElement())) } +final class ChiselHierarchicalBarrier private[chisel] ( + root: Target, + name: String, + instance: Target +) extends ChiselInstanceElement(root, name, instance) { + protected def generateElement(): Element = HierarchicalBarrier(name) +} + final class ChiselLogicRect private[chisel] ( - root: InstanceTarget, + root: Target, name: String, - instance: InstanceTarget, + instance: Target, val width: Constraint[LengthUnit], val height: Constraint[LengthUnit], val area: Constraint[AreaUnit], val aspectRatio: Constraint[Rational], val hardBoundary: Boolean ) extends ChiselInstanceElement(root, name, instance) { - protected def generateElement(): Element = ConstrainedLogicRect(name, width, height, area, aspectRatio, hardBoundary) - } final class ChiselDummyRect private[chisel] ( - root: InstanceTarget, + root: Target, name: String, val width: Constraint[LengthUnit] = Unconstrained[LengthUnit], val height: Constraint[LengthUnit] = Unconstrained[LengthUnit], val area: Constraint[AreaUnit] = Unconstrained[AreaUnit], val aspectRatio: Constraint[Rational] = Unconstrained[Rational] ) extends ChiselDummyElement(root, name) { - protected def generateElement(): Element = ConstrainedDummyRect(name, width, height, area, aspectRatio) - } final class ChiselMem private[chisel] ( - root: InstanceTarget, + root: Target, name: String, reference: ReferenceTarget ) extends ChiselMemElement(root, name, reference) { - protected def generateElement(): Element = MemElement(name) +} + +final class ChiselMemArray private[chisel] ( + root: Target, + name: String, + context: ChiselFloorplanContext +) extends ChiselGroupElement(root, name, context) { + protected def initialSize = 0 + protected def generateGroupElement(names: Seq[Option[String]]): Group = MemElementArray(name, names) + def addMem[T <: Data](mem: MemBase[T]) = this.addElement(this.context.addMem(mem)) } /* final class ChiselWeightedGrid private[chisel] ( - root: InstanceTarget, + root: Target, name: String, val xDim: Int, val yDim: Int, @@ -265,7 +317,7 @@ final class ChiselWeightedGrid private[chisel] ( set(x, y, element, weight) } - def set(x: Int, y: Int, root: InstanceTarget): Unit = set(x, y, module, Rational(1)) + def set(x: Int, y: Int, root: Target): Unit = set(x, y, module, Rational(1)) protected def generateElement(): Element = { _isCommitted = true diff --git a/floorplan/src/main/scala/barstools/floorplan/firrtl/Annotations.scala b/floorplan/src/main/scala/barstools/floorplan/firrtl/Annotations.scala index 75b4cceb..8fd30fe0 100644 --- a/floorplan/src/main/scala/barstools/floorplan/firrtl/Annotations.scala +++ b/floorplan/src/main/scala/barstools/floorplan/firrtl/Annotations.scala @@ -6,12 +6,6 @@ import barstools.floorplan.{Element, Group} import firrtl.annotations._ import firrtl.stage.{RunFirrtlTransformAnnotation} -// John '21: We're going to get rid of ModuleTarget support for now. InstanceTargets may break dedup, but they don't support heterogeneous configs and are -// kind of redundant with InstanceTargets. InstanceTarget behavior is a little more intuitive for writing the Aspects. - -// John '20: To make this a bit easier, I'm going to make floorplan IR embedded in this annotation rather than relying on -// the annotation to serialize the case class correctly (it doesn't currently serialize type parameters, which makes this a bit painful) -// We'll probably want to change this later trait FloorplanAnnotation extends Annotation { val fpir: String } @@ -24,8 +18,8 @@ case class MemFloorplanAnnotation(targets: Seq[Seq[Target]], fpir: String) exten def duplicate(t: Seq[Seq[Target]]) = this.copy(t, fpir) } -case class NoReferenceFloorplanAnnotation(target: InstanceTarget, fpir: String) extends SingleTargetAnnotation[InstanceTarget] with FloorplanAnnotation { - def duplicate(t: InstanceTarget) = this.copy(t, fpir) +case class NoReferenceFloorplanAnnotation(target: Target, fpir: String) extends SingleTargetAnnotation[Target] with FloorplanAnnotation { + def duplicate(t: Target) = this.copy(t, fpir) } object InstanceFloorplanAnnotation { @@ -37,7 +31,7 @@ object MemFloorplanAnnotation { } object NoReferenceFloorplanAnnotation { - def apply(target: InstanceTarget, element: Element): NoReferenceFloorplanAnnotation = NoReferenceFloorplanAnnotation(target, element.serialize) + def apply(target: Target, element: Element): NoReferenceFloorplanAnnotation = NoReferenceFloorplanAnnotation(target, element.serialize) } case class FloorplanIRFileAnnotation(value: String) extends NoTargetAnnotation diff --git a/floorplan/src/main/scala/barstools/floorplan/firrtl/Passes.scala b/floorplan/src/main/scala/barstools/floorplan/firrtl/Passes.scala index ac406468..7d2e1ec6 100644 --- a/floorplan/src/main/scala/barstools/floorplan/firrtl/Passes.scala +++ b/floorplan/src/main/scala/barstools/floorplan/firrtl/Passes.scala @@ -2,10 +2,10 @@ package barstools.floorplan.firrtl import barstools.floorplan.{FloorplanSerialization, FloorplanElementRecord, FloorplanState} -import firrtl.{CircuitState, Transform, DependencyAPIMigration, VerilogEmitter} +import firrtl.{CircuitState, Transform, DependencyAPIMigration, VerilogEmitter, AnnotationSeq} import firrtl.options.{Dependency, RegisteredTransform, ShellOption} import firrtl.analyses.{IRLookup} -import firrtl.annotations.{InstanceTarget, ReferenceTarget, IsComponent} +import firrtl.annotations.{InstanceTarget, ReferenceTarget, ModuleTarget, Target, IsComponent} import firrtl.annotations.TargetToken.{Instance, OfModule} // NOTE: If you rename/add this transform, don't forget to update META-INF @@ -27,18 +27,20 @@ class GenerateFloorplanIRPass extends Transform with RegisteredTransform with De def execute(state: CircuitState): CircuitState = { - def getInstancePath(t: InstanceTarget): String = "/" + t.asPath.toList.map(_._1.value).mkString("/") + def getInstancePath(t: Option[InstanceTarget]): String = t map { it => + "/" + it.asPath.toList.map(_._1.value).mkString("/") + } getOrElse "/" - def getRelativePath(root: InstanceTarget, inst: IsComponent): String = { - val rootPath = root.asPath - val instPath = inst.asPath + def getRelativePath(root: Option[InstanceTarget], inst: Option[IsComponent]): String = { + val rootPath = root.map(_.asPath).getOrElse(Seq()) + val instPath = inst.map(_.asPath).getOrElse(Seq()) assert(instPath.take(rootPath.length) == rootPath, s"InstanceTarget ${instPath} must be inside ${rootPath}") val pathStr = instPath.drop(rootPath.length).toList.map(_._1.value).mkString("/") - inst match { + inst.map(_ match { case x: InstanceTarget => pathStr case x: ReferenceTarget => pathStr + "." + x.ref case _ => ??? // Shouldn't exist - } + }).getOrElse("") } def newRecord(path: String, ref: Option[String], anno: FloorplanAnnotation, ext: Option[String] = None) = @@ -46,13 +48,38 @@ class GenerateFloorplanIRPass extends Transform with RegisteredTransform with De val list = state.annotations.collect({ case x: NoReferenceFloorplanAnnotation => - newRecord(getInstancePath(x.target), None, x) + val rootTarget = x.target match { + case y: InstanceTarget => Some(y) + case y: ModuleTarget => + assert(y.module == state.circuit.main, "ModuleTarget is only supported for the top module") + Option.empty[InstanceTarget] + case _ => ??? + } + newRecord(getInstancePath(rootTarget), None, x) case x: InstanceFloorplanAnnotation if x.targets.flatten.length == 2 => - val rootTarget = x.targets(0)(0).asInstanceOf[InstanceTarget] - val instTarget = x.targets(1)(0).asInstanceOf[InstanceTarget] + val rootTarget = x.targets(0)(0) match { + case y: InstanceTarget => Some(y) + case y: ModuleTarget => + assert(y.module == state.circuit.main, "ModuleTarget is only supported for the top module") + Option.empty[InstanceTarget] + case _ => ??? + } + val instTarget = x.targets(1)(0) match { + case y: InstanceTarget => Some(y) + case y: ModuleTarget => + assert(y.module == state.circuit.main, "ModuleTarget is only supported for the top module") + Option.empty[InstanceTarget] + case _ => ??? + } newRecord(getInstancePath(rootTarget), Some(getRelativePath(rootTarget, instTarget)), x) case x: MemFloorplanAnnotation if x.targets.flatten.length == 2 => - val rootTarget = x.targets(0)(0).asInstanceOf[InstanceTarget] + val rootTarget = x.targets(0)(0) match { + case y: InstanceTarget => Some(y) + case y: ModuleTarget => + assert(y.module == state.circuit.main, "ModuleTarget is only supported for the top module") + Option.empty[InstanceTarget] + case _ => ??? + } val refTarget = x.targets(1)(0).asInstanceOf[ReferenceTarget] // Note: This assumes specific behavior from ReplSeqMem, namely that it replaces the Mem reference with // a wrapper instance named ${ext} that instantiates an external bbox named ${ext}_ext @@ -68,7 +95,7 @@ class GenerateFloorplanIRPass extends Transform with RegisteredTransform with De instance=ext, ofModule=ext, path=refTarget.path :+ (Instance(refTarget.ref), OfModule(mem))) - newRecord(getInstancePath(rootTarget), Some(getRelativePath(rootTarget, newTarget)), x, Some(ext)) + newRecord(getInstancePath(rootTarget), Some(getRelativePath(rootTarget, Some(newTarget))), x, Some(ext)) }) val filename = state.annotations.collectFirst({ diff --git a/src/main/scala/barstools/tapeout/transforms/GenerateTopAndHarness.scala b/src/main/scala/barstools/tapeout/transforms/GenerateTopAndHarness.scala index 4bf5bae5..459b36de 100644 --- a/src/main/scala/barstools/tapeout/transforms/GenerateTopAndHarness.scala +++ b/src/main/scala/barstools/tapeout/transforms/GenerateTopAndHarness.scala @@ -67,6 +67,7 @@ private class GenerateTopAndHarness(annotations: AnnotationSeq) extends LazyLogg val annos = new FirrtlStage().execute( Array.empty, annotations ++ Seq( + RunFirrtlTransformAnnotation(Dependency[FloorplanReParentTransform]), RunFirrtlTransformAnnotation(Dependency[ReParentCircuit]), RunFirrtlTransformAnnotation(Dependency[RemoveUnusedModules]) ) ++ diff --git a/tapeout/src/main/scala/barstools/tapeout/transforms/FloorplanReParentTransform.scala b/tapeout/src/main/scala/barstools/tapeout/transforms/FloorplanReParentTransform.scala new file mode 100644 index 00000000..d2a4a240 --- /dev/null +++ b/tapeout/src/main/scala/barstools/tapeout/transforms/FloorplanReParentTransform.scala @@ -0,0 +1,58 @@ +// See LICENSE for license details +package barstools.tapeout.transforms +// This needs to be in the tapeout package, not floorplan, because of the build dependencies (floorplan cannot depend on tapeout) + +import barstools.floorplan.firrtl.{NoReferenceFloorplanAnnotation, InstanceFloorplanAnnotation, MemFloorplanAnnotation, FloorplanAnnotation} +import firrtl.annotations.{InstanceTarget, ReferenceTarget, ModuleTarget, Target, IsComponent} +import firrtl.{CircuitState, Transform, DependencyAPIMigration, AnnotationSeq} +import firrtl.stage.Forms +import firrtl.options.Dependency +import firrtl.stage.TransformManager.TransformDependency + +class FloorplanReParentTransform extends Transform with DependencyAPIMigration { + + override def prerequisites: Seq[TransformDependency] = Forms.HighForm + override def optionalPrerequisites: Seq[TransformDependency] = Seq.empty + override def optionalPrerequisiteOf: Seq[TransformDependency] = Seq(Dependency[ReParentCircuit]) + override def invalidates(a: Transform): Boolean = false + + def transformTarget(t: Target, top: String): Target = t match { + case it: InstanceTarget => + if (it.ofModule == top) it.ofModuleTarget else it + case ot => ot + } + + def transformTargets(t: Seq[Seq[Target]], top: String): Seq[Seq[Target]] = { + t.map(_.map(x => transformTarget(x, top))) + } + + def execute(state: CircuitState): CircuitState = { + val newTop = state.annotations.collectFirst { + case ReParentCircuitAnnotation(x) => x.module + } + + // Convert the annotated top to a ModuleTarget, otherwise leave them alone and let ReParentCircuit handle them + val newAnnos = newTop.map(top => AnnotationSeq(state.annotations.toSeq.map { _ match { + // We probably want to check the sizes of these first + case a: NoReferenceFloorplanAnnotation => + a.target match { + case t: InstanceTarget => a.copy(target = transformTarget(t, top)) + case t => a + } + case a: InstanceFloorplanAnnotation => + a.targets(0)(0) match { + case t: InstanceTarget => a.copy(targets = transformTargets(a.targets, top)) + case t => a + } + case a: MemFloorplanAnnotation => + a.targets(0)(0) match { + case t: InstanceTarget => a.copy(targets = transformTargets(a.targets, top)) + case t => a + } + case _: FloorplanAnnotation => ??? + case a => a + }})).getOrElse(state.annotations) + + state.copy(annotations = newAnnos) + } +} From 7bb7d2856cfa893fbf431be7c68f3a902b4de6fe Mon Sep 17 00:00:00 2001 From: John Wright Date: Sat, 17 Jul 2021 15:32:10 -0700 Subject: [PATCH 33/73] Replace MemElementArray with MemMacroArray --- .../barstools/floorplan/compiler/Passes.scala | 49 +++++++++++++++++-- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/Passes.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/Passes.scala index 8cc2104c..15efaee6 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/Passes.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/Passes.scala @@ -4,7 +4,9 @@ package barstools.floorplan.compiler import firrtl.annotations.TargetToken.{Instance, OfModule} import barstools.floorplan._ -import scala.collection.Map +import scala.collection.{Map, Set} +import scala.collection.mutable.{HashMap, HashSet} + object FloorplanPasses { @@ -43,13 +45,33 @@ class ResolveConstraintsPass extends Pass { } class TransformMemsPass(instMap: Map[OfModule, Seq[(Instance, OfModule)]]) extends Pass { + + val nameSet = new HashSet[String]() + val renameMap = new HashMap[String, Set[String]]() + + def getUniqueName(suggestion: String): String = { + // This is ugly and probably slow + var i = 0 + var tempName = suggestion + s"_${i}" + while (nameSet.contains(tempName)) { + tempName = suggestion + s"_${i}" + i += 1 + } + nameSet.add(tempName) + tempName + } + def execute(state: FloorplanState): FloorplanState = { - val newRecords = state.records.map({ record => + nameSet ++= state.records.map(_.element.name).toSet + // Need to update in two passes + val newRecords = state.records.flatMap({ record => record.element match { case e: MemElement => // TODO fail gracefully if key does not exist instMap(OfModule(record.memExt.get)).map { case (inst: Instance, ofMod: OfModule) => - val element = AbstractMacro(ofMod.value) + nameSet.remove(e.name) + val element = AbstractMacro(getUniqueName(e.name + "_" + ofMod.value)) + renameMap.update(e.name, renameMap.getOrElse(e.name, Set()) ++ Set(element.name)) FloorplanElementRecord( root = record.root, inst = record.inst.map(_ + "/" + inst.value), @@ -59,7 +81,26 @@ class TransformMemsPass(instMap: Map[OfModule, Seq[(Instance, OfModule)]]) exten } case _ => Seq(record) } - }).flatten + }).map({ record => + record.element match { + case e: MemElementArray => + val element = MemMacroArray( + name = e.name, + elements = e.elements.filter(_.isDefined).flatMap(x => renameMap(x.get)).map(x => Some(x)), + width = e.width, + height = e.height, + area = e.area, + aspectRatio = e.aspectRatio + ) + FloorplanElementRecord( + root = record.root, + inst = record.inst, // should always be None + element = element, + memExt = None + ) + case _ => record + } + }) state.copy(records = newRecords) } } From 4d84447b2c8e496e51d70278be866f19f90a4216 Mon Sep 17 00:00:00 2001 From: John Wright Date: Sat, 17 Jul 2021 15:46:16 -0700 Subject: [PATCH 34/73] Clean up names --- .../compiler/FloorplanCompiler.scala | 4 +- .../barstools/floorplan/compiler/Pass.scala | 40 +++++++++++++++++++ .../{Passes.scala => TransformMemsPass.scala} | 38 +----------------- 3 files changed, 43 insertions(+), 39 deletions(-) create mode 100644 floorplan/src/main/scala/barstools/floorplan/compiler/Pass.scala rename floorplan/src/main/scala/barstools/floorplan/compiler/{Passes.scala => TransformMemsPass.scala} (69%) diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanCompiler.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanCompiler.scala index 1bcba277..82db42d2 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanCompiler.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanCompiler.scala @@ -40,9 +40,9 @@ object FloorplanCompiler extends App { throw new Exception("Error parsing options!") } - // TODO make FloorplanPasses customizable + // TODO make Passes customizable val fpStateIn = FloorplanState.fromFiles(opts.inFiles) - val fpStateOut = FloorplanPasses(opts).foldLeft(fpStateIn) { (state, pass) => pass.execute(state) } + val fpStateOut = Pass.all(opts).foldLeft(fpStateIn) { (state, pass) => pass.execute(state) } FloorplanState.toFile(opts.outFile, opts.outFmt, fpStateOut) } diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/Pass.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/Pass.scala new file mode 100644 index 00000000..923b1d47 --- /dev/null +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/Pass.scala @@ -0,0 +1,40 @@ +// See LICENSE for license details +package barstools.floorplan.compiler + +import barstools.floorplan._ + +object Pass { + + def all(opts: FloorplanOptions): Seq[Pass] = { + val instMap = MemInstMap.fromFiles(opts.memInstMapFiles) + val sbAnnos = Seq() // TODO + Seq( + new SidebandAnnotationPass(sbAnnos), + new TransformMemsPass(instMap), + new TopDownPropagationPass, + new BottomUpPropagationPass, + new ResolveConstraintsPass + ) + } +} + +abstract class Pass { + def execute(state: FloorplanState): FloorplanState +} + +class SidebandAnnotationPass(annos: Seq[SidebandAnnotation]) extends Pass { + def execute(state: FloorplanState): FloorplanState = state // TODO +} + +class TopDownPropagationPass extends Pass { + def execute(state: FloorplanState): FloorplanState = state // TODO +} + +class BottomUpPropagationPass extends Pass { + def execute(state: FloorplanState): FloorplanState = state // TODO +} + +class ResolveConstraintsPass extends Pass { + def execute(state: FloorplanState): FloorplanState = state // TODO +} + diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/Passes.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/TransformMemsPass.scala similarity index 69% rename from floorplan/src/main/scala/barstools/floorplan/compiler/Passes.scala rename to floorplan/src/main/scala/barstools/floorplan/compiler/TransformMemsPass.scala index 15efaee6..dda8df05 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/Passes.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/TransformMemsPass.scala @@ -4,46 +4,10 @@ package barstools.floorplan.compiler import firrtl.annotations.TargetToken.{Instance, OfModule} import barstools.floorplan._ + import scala.collection.{Map, Set} import scala.collection.mutable.{HashMap, HashSet} - -object FloorplanPasses { - - // TODO make this customizable - def apply(opts: FloorplanOptions): Seq[Pass] = { - val instMap = MemInstMap.fromFiles(opts.memInstMapFiles) - val sbAnnos = Seq() // TODO - Seq( - new SidebandAnnotationPass(sbAnnos), - new TransformMemsPass(instMap), - new TopDownPropagationPass, - new BottomUpPropagationPass, - new ResolveConstraintsPass - ) - } -} - -abstract class Pass { - def execute(state: FloorplanState): FloorplanState -} - -class SidebandAnnotationPass(annos: Seq[SidebandAnnotation]) extends Pass { - def execute(state: FloorplanState): FloorplanState = state // TODO -} - -class TopDownPropagationPass extends Pass { - def execute(state: FloorplanState): FloorplanState = state // TODO -} - -class BottomUpPropagationPass extends Pass { - def execute(state: FloorplanState): FloorplanState = state // TODO -} - -class ResolveConstraintsPass extends Pass { - def execute(state: FloorplanState): FloorplanState = state // TODO -} - class TransformMemsPass(instMap: Map[OfModule, Seq[(Instance, OfModule)]]) extends Pass { val nameSet = new HashSet[String]() From d5fe7bd32a6a1eaed86732570ee983a7fc6db3b4 Mon Sep 17 00:00:00 2001 From: John Wright Date: Sat, 17 Jul 2021 19:10:37 -0700 Subject: [PATCH 35/73] Add first pass at HammerIR serialization --- ...ion.scala => FloorplanSerialization.scala} | 0 .../barstools/floorplan/FloorplanState.scala | 22 ++-- .../main/scala/barstools/floorplan/IR.scala | 70 +++++++++--- .../barstools/floorplan/chisel/API.scala | 14 +-- .../compiler/TransformMemsPass.scala | 2 +- .../barstools/floorplan/hammer/HammerIR.scala | 105 ++++++++++++++++++ .../hammer/HammerSerialization.scala | 36 ++++++ 7 files changed, 215 insertions(+), 34 deletions(-) rename floorplan/src/main/scala/barstools/floorplan/{Serialization.scala => FloorplanSerialization.scala} (100%) create mode 100644 floorplan/src/main/scala/barstools/floorplan/hammer/HammerIR.scala create mode 100644 floorplan/src/main/scala/barstools/floorplan/hammer/HammerSerialization.scala diff --git a/floorplan/src/main/scala/barstools/floorplan/Serialization.scala b/floorplan/src/main/scala/barstools/floorplan/FloorplanSerialization.scala similarity index 100% rename from floorplan/src/main/scala/barstools/floorplan/Serialization.scala rename to floorplan/src/main/scala/barstools/floorplan/FloorplanSerialization.scala diff --git a/floorplan/src/main/scala/barstools/floorplan/FloorplanState.scala b/floorplan/src/main/scala/barstools/floorplan/FloorplanState.scala index 422ea763..7531e3eb 100644 --- a/floorplan/src/main/scala/barstools/floorplan/FloorplanState.scala +++ b/floorplan/src/main/scala/barstools/floorplan/FloorplanState.scala @@ -1,9 +1,12 @@ // See LICENSE for license details package barstools.floorplan +import barstools.floorplan.hammer.HammerIR import java.io.{File, FileWriter} -final case class FloorplanElementRecord(root: String, inst: Option[String], element: Element, memExt: Option[String] = None) +final case class FloorplanElementRecord(root: String, inst: Option[String], element: Element, memExt: Option[String] = None) { + def fullPath = root + inst.map(x => "/" + x).getOrElse("") +} final case class FloorplanState(records: Seq[FloorplanElementRecord], level: Int) @@ -23,24 +26,19 @@ object FloorplanState { fpState } + def fromFiles(files: Seq[String]): FloorplanState = { + assert(files.length == 1, "FIXME combine multiple states into one") + fromFile(files(0)) + } + def toFile(file: String, fmt: OutputFormat, state: FloorplanState) { val writer = new FileWriter(new File(file)) fmt match { - case OutputFormat.HammerIR => writer.write(FloorplanState.toHammerIR(state)) + case OutputFormat.HammerIR => writer.write(HammerIR.serialize(HammerIR.fromFloorplanState(state))) case OutputFormat.FloorplanIR => writer.write(FloorplanState.serialize(state)) } writer.close() } - - def fromFiles(files: Seq[String]): FloorplanState = { - assert(files.length == 1, "FIXME combine multiple states into one") - fromFile(files(0)) - } - - def toHammerIR(state: FloorplanState): String = { - assert(state.level == 0, "Can only convert level 0 FloorplanState") - ??? - } } sealed trait OutputFormat diff --git a/floorplan/src/main/scala/barstools/floorplan/IR.scala b/floorplan/src/main/scala/barstools/floorplan/IR.scala index 9cb46e1e..6eaeb665 100644 --- a/floorplan/src/main/scala/barstools/floorplan/IR.scala +++ b/floorplan/src/main/scala/barstools/floorplan/IR.scala @@ -25,7 +25,7 @@ sealed abstract class Group extends Element { private[floorplan] final case class HierarchicalBarrier( name: String ) extends Primitive { - final def level = 0 // Maybe 1 + final def level = 0 } ////////////////////////////////////////////// Rectangular things @@ -37,28 +37,39 @@ trait ConstrainedRectLike { def aspectRatio: Constraint[Rational] } -trait ConcreteRectLike { +trait SizedRectLike { + def width: LengthUnit + def height: LengthUnit +} + +trait PlacedRectLike { + def x: LengthUnit + def y: LengthUnit def width: LengthUnit def height: LengthUnit } object IRLevel { - def max = 2 + def max = 3 } sealed abstract class AbstractRectPrimitive extends Primitive { - final def level = 2 + final def level = 3 } sealed abstract class ConstrainedRectPrimitive extends Primitive with ConstrainedRectLike { + final def level = 2 +} + +sealed abstract class SizedRectPrimitive extends Primitive with SizedRectLike { final def level = 1 } -sealed abstract class ConcreteRectPrimitive extends Primitive with ConcreteRectLike { +sealed abstract class PlacedRectPrimitive extends Primitive with PlacedRectLike { final def level = 0 } -private[floorplan] final case class ConstrainedDummyRect( +private[floorplan] final case class ConstrainedSpacerRect( name: String, width: Constraint[LengthUnit] = Unconstrained[LengthUnit], height: Constraint[LengthUnit] = Unconstrained[LengthUnit], @@ -66,6 +77,16 @@ private[floorplan] final case class ConstrainedDummyRect( aspectRatio: Constraint[Rational] = Unconstrained[Rational] ) extends ConstrainedRectPrimitive +private[floorplan] final case class SizedSpacerRect( + name: String, + x: LengthUnit, + y: LengthUnit, + width: LengthUnit, + height: LengthUnit +) extends SizedRectPrimitive + +// No PlacedSpacerRect exists because they're only for spacing + private[floorplan] final case class ConstrainedLogicRect( name: String, width: Constraint[LengthUnit], @@ -75,12 +96,21 @@ private[floorplan] final case class ConstrainedLogicRect( hardBoundary: Boolean ) extends ConstrainedRectPrimitive -private[floorplan] final case class ConcreteLogicRect( +private[floorplan] final case class SizedLogicRect( name: String, width: LengthUnit, height: LengthUnit, hardBoundary: Boolean -) extends ConcreteRectPrimitive +) extends SizedRectPrimitive + +private[floorplan] final case class PlacedLogicRect( + name: String, + x: LengthUnit, + y: LengthUnit, + width: LengthUnit, + height: LengthUnit, + hardBoundary: Boolean +) extends PlacedRectPrimitive ////////////////////////////////////////////// Aggregate (Group) things @@ -103,7 +133,7 @@ private[floorplan] final case class WeightedGrid( weights: Seq[Rational], packed: Boolean ) extends Grid { - def level = 1 + def level = 2 } @@ -123,7 +153,7 @@ private[floorplan] final case class MemElementArray( area: Constraint[AreaUnit] = Unconstrained[AreaUnit], aspectRatio: Constraint[Rational] = Unconstrained[Rational] ) extends Group with ConstrainedRectLike { - def level = 2 + def level = 3 } // Container for MemElements that have been converted to Macros @@ -138,19 +168,31 @@ private[floorplan] final case class MemMacroArray( area: Constraint[AreaUnit] = Unconstrained[AreaUnit], aspectRatio: Constraint[Rational] = Unconstrained[Rational] ) extends Group with ConstrainedRectLike { - def level = 1 + def level = 2 } // Reference to a macro blackbox with unknown dimensions // Do not use for SyncReadMem objects; use MemElement instead private[floorplan] final case class AbstractMacro ( - name: String + name: String, + ofModule: String ) extends AbstractRectPrimitive // Reference to a macro blackbox that has known dimensions -private[floorplan] final case class ConcreteMacro ( +private[floorplan] final case class SizedMacro ( + name: String, + ofModule: String, + width: LengthUnit, + height: LengthUnit +) extends SizedRectPrimitive + +// Reference to a macro blackbox that has known dimensions and has been placed +private[floorplan] final case class PlacedMacro ( name: String, + ofModule: String, + x: LengthUnit, + y: LengthUnit, width: LengthUnit, height: LengthUnit -) extends ConcreteRectPrimitive +) extends PlacedRectPrimitive diff --git a/floorplan/src/main/scala/barstools/floorplan/chisel/API.scala b/floorplan/src/main/scala/barstools/floorplan/chisel/API.scala index c6a339aa..c4bacabc 100644 --- a/floorplan/src/main/scala/barstools/floorplan/chisel/API.scala +++ b/floorplan/src/main/scala/barstools/floorplan/chisel/API.scala @@ -38,15 +38,15 @@ final class ChiselFloorplanContext private[chisel] (val root: Target, topElement addElement(elt) } - def createDummy( + def createSpacer( name: Option[String] = None, width: Constraint[LengthUnit] = Unconstrained[LengthUnit], height: Constraint[LengthUnit] = Unconstrained[LengthUnit], area: Constraint[AreaUnit] = Unconstrained[AreaUnit], aspectRatio: Constraint[Rational] = Unconstrained[Rational] - ): ChiselDummyRect = { + ): ChiselSpacerRect = { val nameStr = FloorplanDatabase.getUnusedName(root, name) - val elt = new ChiselDummyRect(root, nameStr, width, height, area, aspectRatio) + val elt = new ChiselSpacerRect(root, nameStr, width, height, area, aspectRatio) addElement(elt) } @@ -189,7 +189,7 @@ sealed abstract class ChiselElement(val root: Target, val name: String) { def getAnnotations(): Seq[Annotation] = getFloorplanAnnotations() } -sealed abstract class ChiselDummyElement(root: Target, name: String) extends ChiselElement(root, name) { +sealed abstract class ChiselSpacerElement(root: Target, name: String) extends ChiselElement(root, name) { private[chisel] def getFloorplanAnnotations() = Seq(NoReferenceFloorplanAnnotation(root, generateElement())) } @@ -250,15 +250,15 @@ final class ChiselLogicRect private[chisel] ( protected def generateElement(): Element = ConstrainedLogicRect(name, width, height, area, aspectRatio, hardBoundary) } -final class ChiselDummyRect private[chisel] ( +final class ChiselSpacerRect private[chisel] ( root: Target, name: String, val width: Constraint[LengthUnit] = Unconstrained[LengthUnit], val height: Constraint[LengthUnit] = Unconstrained[LengthUnit], val area: Constraint[AreaUnit] = Unconstrained[AreaUnit], val aspectRatio: Constraint[Rational] = Unconstrained[Rational] -) extends ChiselDummyElement(root, name) { - protected def generateElement(): Element = ConstrainedDummyRect(name, width, height, area, aspectRatio) +) extends ChiselSpacerElement(root, name) { + protected def generateElement(): Element = ConstrainedSpacerRect(name, width, height, area, aspectRatio) } final class ChiselMem private[chisel] ( diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/TransformMemsPass.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/TransformMemsPass.scala index dda8df05..5d77c1e3 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/TransformMemsPass.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/TransformMemsPass.scala @@ -34,7 +34,7 @@ class TransformMemsPass(instMap: Map[OfModule, Seq[(Instance, OfModule)]]) exten // TODO fail gracefully if key does not exist instMap(OfModule(record.memExt.get)).map { case (inst: Instance, ofMod: OfModule) => nameSet.remove(e.name) - val element = AbstractMacro(getUniqueName(e.name + "_" + ofMod.value)) + val element = AbstractMacro(getUniqueName(e.name + "_" + ofMod.value), ofModule = ofMod.value) renameMap.update(e.name, renameMap.getOrElse(e.name, Set()) ++ Set(element.name)) FloorplanElementRecord( root = record.root, diff --git a/floorplan/src/main/scala/barstools/floorplan/hammer/HammerIR.scala b/floorplan/src/main/scala/barstools/floorplan/hammer/HammerIR.scala new file mode 100644 index 00000000..e7a1dffe --- /dev/null +++ b/floorplan/src/main/scala/barstools/floorplan/hammer/HammerIR.scala @@ -0,0 +1,105 @@ +// See LICENSE for license details +package barstools.floorplan.hammer + +import barstools.floorplan._ + +import java.io.{Writer} + +// This implements a small subset of HammerIR + +object HammerIR { + + def fromFloorplanState(state: FloorplanState): HammerIR = { + assert(state.level == 0, "Can only convert level 0 FloorplanState") + val placementConstraints = state.records.map({ record => + record.element match { + case c: PlacedMacro => + Some(PlacementConstraint( + path = record.fullPath, + typ = PlacementType.hardmacro, + orientation = Orientation.r0, // TODO represent this in FPIR + x = toMicrons(c.x), + y = toMicrons(c.y), + create_physical = Some(false), + width = toMicrons(c.width), + height = toMicrons(c.height), + master = Some(c.ofModule), + margins = Some(PlacementMargins(0.0,0.0,0.0,0.0)), // TODO represent this in FPIR + top_layer = None, // TODO need this for macros + layers = None, // TODO need this for macros + obs_types = None // TODO need this for macros + )) + case c: PlacedLogicRect => + val isTop = (record.root.split("/").length == 1) && (record.inst.get.split("/").length == 1) + Some(PlacementConstraint( + path = record.fullPath, + typ = if (isTop) PlacementType.toplevel else PlacementType.placement, + orientation = Orientation.r0, // TODO represent this in FPIR + x = toMicrons(c.x), + y = toMicrons(c.y), + create_physical = Some(false), + width = toMicrons(c.width), + height = toMicrons(c.height), + master = None, + margins = if (isTop) Some(PlacementMargins(0.0,0.0,0.0,0.0)) else None, // TODO represent this in FPIR + top_layer = None, + layers = None, + obs_types = None + )) + case c => None + } + }).filter(_.isDefined).map(_.get) + HammerIR(placementConstraints = placementConstraints) + } + + def serialize(h: HammerIR): String = HammerSerialization.serialize(h) + + def deserialize(s: String): HammerIR = HammerSerialization.deserialize(s) + + def toMicrons(l: LengthUnit): Double = { + 0.0 // TODO + } +} + +final case class HammerIR private[hammer] ( + placementConstraints: Seq[PlacementConstraint] +) + +final case class PlacementConstraint private[hammer] ( + path: String, + typ: PlacementType.Value, // type is a keyword, so we use typ and rename it in the serializer + orientation: Orientation.Value, + // TODO these should ideally all be decimal types, not doubles + x: Double, + y: Double, + width: Double, + height: Double, + // End TODO + master: Option[String], + create_physical: Option[Boolean], + margins: Option[PlacementMargins], + top_layer: Option[String], + layers: Option[Seq[String]], + obs_types: Option[Seq[ObstructionType.Value]] +) + +object PlacementType extends Enumeration { + val dummy, placement, toplevel, hardmacro, hierarchical, obstruction = Value +} + +object Orientation extends Enumeration { + val r0, r90, r180, r270, mx, mx90, my, my90 = Value +} + +object ObstructionType extends Enumeration { + val place, route, power = Value +} + +final case class PlacementMargins private[hammer] ( + // TODO these should ideally all be decimal types, not doubles + left: Double, + right: Double, + top: Double, + bottom: Double +) + diff --git a/floorplan/src/main/scala/barstools/floorplan/hammer/HammerSerialization.scala b/floorplan/src/main/scala/barstools/floorplan/hammer/HammerSerialization.scala new file mode 100644 index 00000000..f3ca1847 --- /dev/null +++ b/floorplan/src/main/scala/barstools/floorplan/hammer/HammerSerialization.scala @@ -0,0 +1,36 @@ +// See LICENSE for license details +package barstools.floorplan.hammer + +import org.json4s._ +import org.json4s.FieldSerializer.renameTo +import org.json4s.native.Serialization.{read, write, writePretty} +import org.json4s.JsonAST.JString + +object HammerSerialization { + + /* + // Custom serializer for PlacementType enums + class PlacementTypeSerializer extends CustomSerializer[PlacementType.Value](format => ( + { case JString(s) => PlacementType.values.withName(s) }, + { case v: PlacementType.Value => JString(v.toString) } + )) + // Custom serializer for Orientation enums + class OrientationSerializer extends CustomSerializer[Orientation.Value](format => ( + { case JString(s) => Orientation.values.withName(s) }, + { case v: Orientation.Value => JString(v.toString) } + )) + */ + + val formats = + DefaultFormats.skippingEmptyValues + + FieldSerializer[PlacementConstraint](renameTo("typ", "type")) + + FieldSerializer[HammerIR](renameTo("placementConstraints", "vlsi.inputs.placement_constraints")) + + def serialize(state: HammerIR): String = writePretty(state)(formats) + + def deserialize(str: String): HammerIR = { + implicit val formats = HammerSerialization.formats + read[HammerIR](str) + } + +} From e0150eef42505eb8982229f9d87e10c6235c6e08 Mon Sep 17 00:00:00 2001 From: John Wright Date: Sat, 17 Jul 2021 21:15:26 -0700 Subject: [PATCH 36/73] Simplify units, no longer separate area and length --- .../barstools/floorplan/Constraints.scala | 429 +++--------------- .../floorplan/FloorplanSerialization.scala | 4 +- .../main/scala/barstools/floorplan/IR.scala | 88 ++-- .../barstools/floorplan/chisel/API.scala | 61 +-- .../barstools/floorplan/hammer/HammerIR.scala | 3 +- .../scala/barstools/floorplan/package.scala | 16 + 6 files changed, 154 insertions(+), 447 deletions(-) create mode 100644 floorplan/src/main/scala/barstools/floorplan/package.scala diff --git a/floorplan/src/main/scala/barstools/floorplan/Constraints.scala b/floorplan/src/main/scala/barstools/floorplan/Constraints.scala index 3fc303b4..e924aabe 100644 --- a/floorplan/src/main/scala/barstools/floorplan/Constraints.scala +++ b/floorplan/src/main/scala/barstools/floorplan/Constraints.scala @@ -1,414 +1,109 @@ // See LICENSE for license details package barstools.floorplan -private[floorplan] final case class IllegalUnitArithmeticException(a: HasUnit, b: HasUnit, op: String) extends Exception(s"Cannot ${op} ${a} and ${b}") - -sealed trait HasUnit { - - def *(that: HasUnit): HasUnit - def /(that: HasUnit): HasUnit - def +(that: HasUnit): HasUnit - def -(that: HasUnit): HasUnit - def >(that: HasUnit): Boolean - def <(that: HasUnit): Boolean - def >=(that: HasUnit): Boolean - def <=(that: HasUnit): Boolean - def %(that: HasUnit): HasUnit - def %?(that: HasUnit): Boolean - def gcd(that: HasUnit): HasUnit - def lcm(that: HasUnit): HasUnit - - def isPositive: Boolean - -} - -object Rational { - - def reduced(num: BigInt, denom: BigInt): Rational = { - if (num == BigInt(0)) { - Rational(BigInt(0), BigInt(1)) - } else { - val gcd = num.gcd(denom) - Rational(num/gcd, denom/gcd) - } - } - - def apply(ratio: Double, decimalPrecision: Long = 1000): Rational = { - Rational.reduced(BigInt(scala.math.round(ratio*decimalPrecision)), BigInt(decimalPrecision)) - } - - def apply(l: Long): Rational = Rational(l, 1) - -} - -// TODO how to cleanly round when dividing? -final case class Rational(num: BigInt, denom: BigInt) extends HasUnit { - - assert(denom != 0, "Cannot divide by zero!") - - def invert = Rational(denom, num) - def isZero = num == BigInt(0) - def isPositive = (this.num*this.denom) > BigInt(0) - - private def lcm(a: BigInt, b: BigInt) = (a*b)/a.gcd(b) - - def *(that: HasUnit) = that match { - case that: Rational => Rational.reduced(this.num * that.num, this.denom * that.denom) - case that: LengthUnit => LengthUnit(this.num * that.value / this.denom) - case that: AreaUnit => AreaUnit(this.num * that.value / this.denom) - case _ => throw new IllegalUnitArithmeticException(this, that, "*") - } - - def /(that: HasUnit) = that match { - case that: Rational => Rational.reduced(this.num * that.denom, this.denom * that.num) - case _ => throw new IllegalUnitArithmeticException(this, that, "/") - } - - def +(that: HasUnit) = that match { - case that: Rational => { - val newDenom = lcm(this.denom, that.denom) - val newNum = (this.num * (newDenom / this.denom)) + (that.num * (newDenom / that.denom)) - Rational.reduced(newNum, newDenom) - } - case _ => throw new IllegalUnitArithmeticException(this, that, "+") - } - - def -(that: HasUnit) = that match { - case that: Rational => { - val newDenom = lcm(this.denom, that.denom) - val newNum = (this.num * (newDenom / this.denom)) - (that.num * (newDenom / that.denom)) - Rational.reduced(newNum, newDenom) - } - case _ => throw new IllegalUnitArithmeticException(this, that, "-") - } - - def >(that: HasUnit) = that match { - case that: Rational => { - val newDenom = lcm(this.denom, that.denom) - (this.num * (newDenom / this.denom)) > (that.num * (newDenom / that.denom)) - } - case _ => throw new IllegalUnitArithmeticException(this, that, ">") - } - - def <(that: HasUnit) = that match { - case that: Rational => { - val newDenom = lcm(this.denom, that.denom) - (this.num * (newDenom / this.denom)) < (that.num * (newDenom / that.denom)) - } - case _ => throw new IllegalUnitArithmeticException(this, that, "<") - } - - def >=(that: HasUnit) = that match { - case that: Rational => { - val newDenom = lcm(this.denom, that.denom) - (this.num * (newDenom / this.denom)) >= (that.num * (newDenom / that.denom)) - } - case _ => throw new IllegalUnitArithmeticException(this, that, ">=") - } - - def <=(that: HasUnit) = that match { - case that: Rational => { - val newDenom = lcm(this.denom, that.denom) - (this.num * (newDenom / this.denom)) <= (that.num * (newDenom / that.denom)) - } - case _ => throw new IllegalUnitArithmeticException(this, that, "<=") - } - - def %(that: HasUnit) = that match { - case that: Rational => { - val newDenom = lcm(this.denom, that.denom) - val newNum = (this.num * (newDenom / this.denom)) % (that.num * (newDenom / that.denom)) - Rational.reduced(newNum, newDenom) - } - case _ => throw new IllegalUnitArithmeticException(this, that, "%") - } - - def %?(that: HasUnit) = that match { - case that: Rational => (this % that).asInstanceOf[Rational].isZero - case _ => throw new IllegalUnitArithmeticException(this, that, "%?") - } - - def gcd(that: HasUnit) = that match { - case that: Rational => { - val newDenom = lcm(this.denom, that.denom) - val newNum = (this.num * (newDenom / this.denom)).gcd(that.num * (newDenom / that.denom)) - Rational.reduced(newNum, newDenom) - } - case _ => throw new IllegalUnitArithmeticException(this, that, "gcd") - } - - def lcm(that: HasUnit) = that match { - case that: Rational => { - val newDenom = lcm(this.denom, that.denom) - val newNum = lcm(this.num * (newDenom / this.denom), that.num * (newDenom / that.denom)) - Rational.reduced(newNum, newDenom) - } - case _ => throw new IllegalUnitArithmeticException(this, that, "lcm") - } - -} - -final case class LengthUnit private[floorplan] (value: BigInt) extends HasUnit { - - def isPositive = this.value > BigInt(0) - - def *(that: HasUnit) = that match { - case that: Rational => LengthUnit((this.value * that.num) / that.denom) - case that: LengthUnit => AreaUnit(this.value * that.value) - case _ => throw new IllegalUnitArithmeticException(this, that, "*") - } - - def /(that: HasUnit) = that match { - case that: Rational => LengthUnit((this.value * that.denom) / that.num) - case that: LengthUnit => Rational.reduced(this.value, that.value) - case _ => throw new IllegalUnitArithmeticException(this, that, "/") - } - - def +(that: HasUnit) = that match { - case that: LengthUnit => LengthUnit(this.value + that.value) - case _ => throw new IllegalUnitArithmeticException(this, that, "+") - } - - def -(that: HasUnit) = that match { - case that: LengthUnit => LengthUnit(this.value - that.value) - case _ => throw new IllegalUnitArithmeticException(this, that, "-") - } - - def >(that: HasUnit) = that match { - case that: LengthUnit => (this.value > that.value) - case _ => throw new IllegalUnitArithmeticException(this, that, ">") - } - - def <(that: HasUnit) = that match { - case that: LengthUnit => (this.value < that.value) - case _ => throw new IllegalUnitArithmeticException(this, that, "<") - } - - def >=(that: HasUnit) = that match { - case that: LengthUnit => (this.value >= that.value) - case _ => throw new IllegalUnitArithmeticException(this, that, ">=") - } - - def <=(that: HasUnit) = that match { - case that: LengthUnit => (this.value <= that.value) - case _ => throw new IllegalUnitArithmeticException(this, that, "<=") - } - - def %(that: HasUnit) = that match { - case that: LengthUnit => LengthUnit(this.value % that.value) - case _ => throw new IllegalUnitArithmeticException(this, that, "%") - } - - def %?(that: HasUnit) = that match { - case that: LengthUnit => (this % that).asInstanceOf[LengthUnit].value == BigInt(0) - case _ => throw new IllegalUnitArithmeticException(this, that, "%") - } - - def gcd(that: HasUnit) = that match { - case that: LengthUnit => LengthUnit(this.value.gcd(that.value)) - case _ => throw new IllegalUnitArithmeticException(this, that, "gcd") - } - - def lcm(that: HasUnit) = that match { - case that: LengthUnit => LengthUnit(this.value*that.value/this.value.gcd(that.value)) - case _ => throw new IllegalUnitArithmeticException(this, that, "lcm") - } +import scala.math.{BigInt, BigDecimal} +sealed trait Constraint { + def and(that: Constraint): Constraint } -final case class AreaUnit private[floorplan] (value: BigInt) extends HasUnit { - - def isPositive = this.value > BigInt(0) - - def *(that: HasUnit) = that match { - case that: Rational => AreaUnit((this.value * that.num) / that.denom) - case _ => throw new IllegalUnitArithmeticException(this, that, "*") - } - - def /(that: HasUnit) = that match { - case that: Rational => AreaUnit((this.value * that.denom) / that.num) - case that: LengthUnit => LengthUnit(this.value / that.value) - case that: AreaUnit => Rational.reduced(this.value, that.value) - case _ => throw new IllegalUnitArithmeticException(this, that, "/") - } - - def +(that: HasUnit) = that match { - case that: AreaUnit => AreaUnit(this.value + that.value) - case _ => throw new IllegalUnitArithmeticException(this, that, "+") - } - - def -(that: HasUnit) = that match { - case that: AreaUnit => AreaUnit(this.value - that.value) - case _ => throw new IllegalUnitArithmeticException(this, that, "-") - } - - def >(that: HasUnit) = that match { - case that: AreaUnit => (this.value > that.value) - case _ => throw new IllegalUnitArithmeticException(this, that, ">") - } - - def <(that: HasUnit) = that match { - case that: AreaUnit => (this.value < that.value) - case _ => throw new IllegalUnitArithmeticException(this, that, "<") - } - - def >=(that: HasUnit) = that match { - case that: AreaUnit => (this.value >= that.value) - case _ => throw new IllegalUnitArithmeticException(this, that, ">=") - } - - def <=(that: HasUnit) = that match { - case that: AreaUnit => (this.value <= that.value) - case _ => throw new IllegalUnitArithmeticException(this, that, "<=") - } - - def %(that: HasUnit) = that match { - case that: AreaUnit => AreaUnit(this.value % that.value) - case _ => throw new IllegalUnitArithmeticException(this, that, "%") - } - - def %?(that: HasUnit) = that match { - case that: AreaUnit => (this % that).asInstanceOf[AreaUnit].value == BigInt(0) - case _ => throw new IllegalUnitArithmeticException(this, that, "%") - } - - def gcd(that: HasUnit) = that match { - case that: AreaUnit => AreaUnit(this.value.gcd(that.value)) - case _ => throw new IllegalUnitArithmeticException(this, that, "gcd") - } - - def lcm(that: HasUnit) = that match { - case that: AreaUnit => AreaUnit(this.value*that.value/this.value.gcd(that.value)) - case _ => throw new IllegalUnitArithmeticException(this, that, "lcm") - } - -} - -sealed trait Constraint[T <: HasUnit] { - - def and(that: Constraint[T]): Constraint[T] - -} - -sealed abstract class PrimitiveConstraint[T <: HasUnit] extends Constraint[T] - -final class Unconstrained[T <: HasUnit] extends PrimitiveConstraint[T] { - - def and(that: Constraint[T]) = that +sealed abstract class PrimitiveConstraint extends Constraint +final class Unconstrained extends PrimitiveConstraint { + def and(that: Constraint) = that } object Unconstrained { - - def apply[T <: HasUnit] = new Unconstrained[T] - + private val u = new Unconstrained + def apply() = u } -final case class EqualTo[T <: HasUnit](unit: T) extends PrimitiveConstraint[T] { - - assert(unit.isPositive, "EqualTo value must be postiive") - - def and(that: Constraint[T]) = that match { - case that: EqualTo[T] => if (this.unit == that.unit) this else ImpossibleConstraint(this, that) - case that: GreaterThanOrEqualTo[T] => if (this.unit >= that.unit) this else ImpossibleConstraint(this, that) - case that: LessThanOrEqualTo[T] => if (this.unit <= that.unit) this else ImpossibleConstraint(this, that) - case that: MultipleOf[T] => if (this.unit %? that.unit) this else ImpossibleConstraint(this, that) - case that: ImpossibleConstraint[T] => that - case that: Unconstrained[T] => this +final case class EqualTo(value: BigDecimal) extends PrimitiveConstraint { + def and(that: Constraint) = that match { + case that: EqualTo => if (this.value == that.value) this else ImpossibleConstraint(this, that) + case that: GreaterThanOrEqualTo => if (this.value >= that.value) this else ImpossibleConstraint(this, that) + case that: LessThanOrEqualTo => if (this.value <= that.value) this else ImpossibleConstraint(this, that) + case that: MultipleOf => if ((this.value % that.value) == BigDecimal(0)) this else ImpossibleConstraint(this, that) + case that: ImpossibleConstraint => that + case that: Unconstrained => this case that => AndConstraint(this, that) } - } -final case class GreaterThanOrEqualTo[T <: HasUnit](unit: T) extends PrimitiveConstraint[T] { - - assert(unit.isPositive , "GreaterThanOrEqualTo value must be postiive") - - def and(that: Constraint[T]) = that match { - case that: EqualTo[T] => if (this.unit <= that.unit) that else ImpossibleConstraint(this, that) - case that: GreaterThanOrEqualTo[T] => if (this.unit >= that.unit) this else that - case that: LessThanOrEqualTo[T] => if (this.unit < that.unit) AndConstraint(this, that) else - if (this.unit == that.unit) EqualTo(this.unit) else ImpossibleConstraint(this, that) - case that: ImpossibleConstraint[T] => that - case that: Unconstrained[T] => this +final case class GreaterThanOrEqualTo(value: BigDecimal) extends PrimitiveConstraint { + def and(that: Constraint) = that match { + case that: EqualTo => if (this.value <= that.value) that else ImpossibleConstraint(this, that) + case that: GreaterThanOrEqualTo => if (this.value >= that.value) this else that + case that: LessThanOrEqualTo => if (this.value < that.value) AndConstraint(this, that) else + if (this.value == that.value) EqualTo(this.value) else ImpossibleConstraint(this, that) + case that: ImpossibleConstraint => that + case that: Unconstrained => this case that => AndConstraint(this, that) } - } -final case class LessThanOrEqualTo[T <: HasUnit](unit: T) extends PrimitiveConstraint[T] { - - assert(unit.isPositive, "LessThanOrEqualTo value must be positive") - - def and(that: Constraint[T]) = that match { - case that: EqualTo[T] => if (this.unit >= that.unit) that else ImpossibleConstraint(this, that) - case that: GreaterThanOrEqualTo[T] => if (this.unit > that.unit) AndConstraint(this, that) else - if (this.unit == that.unit) EqualTo(this.unit) else ImpossibleConstraint(this, that) - case that: LessThanOrEqualTo[T] => if (this.unit <= that.unit) this else that - case that: ImpossibleConstraint[T] => that - case that: Unconstrained[T] => this +final case class LessThanOrEqualTo(value: BigDecimal) extends PrimitiveConstraint { + def and(that: Constraint) = that match { + case that: EqualTo => if (this.value >= that.value) that else ImpossibleConstraint(this, that) + case that: GreaterThanOrEqualTo => if (this.value > that.value) AndConstraint(this, that) else + if (this.value == that.value) EqualTo(this.value) else ImpossibleConstraint(this, that) + case that: LessThanOrEqualTo => if (this.value <= that.value) this else that + case that: ImpossibleConstraint => that + case that: Unconstrained => this case that => AndConstraint(this, that) } - } // TODO allow offset -final case class MultipleOf[T <: HasUnit](unit: T) extends PrimitiveConstraint[T] { - - assert(unit.isPositive, "MultipleOf value must be positive") - - def and(that: Constraint[T]) = that match { - case that: EqualTo[T] => if (that.unit %? this.unit) that else ImpossibleConstraint(this, that) - case that: MultipleOf[T] => MultipleOf((this.unit lcm that.unit).asInstanceOf[T]) - case that: LessThanOrEqualTo[T] => if (that.unit < this.unit) ImpossibleConstraint(this, that) else AndConstraint(this, that) - case that: ImpossibleConstraint[T] => that - case that: Unconstrained[T] => this +final case class MultipleOf(value: BigDecimal) extends PrimitiveConstraint { + def and(that: Constraint) = that match { + case that: EqualTo => if ((that.value % this.value) == BigDecimal(0)) that else ImpossibleConstraint(this, that) + case that: MultipleOf => MultipleOf(lcm(this.value,that.value)) + case that: LessThanOrEqualTo => if (that.value < this.value) ImpossibleConstraint(this, that) else AndConstraint(this, that) + case that: ImpossibleConstraint => that + case that: Unconstrained => this case that => AndConstraint(this, that) } - } -sealed abstract class AggregateConstraint[T <: HasUnit] extends Constraint[T] - -final case class ImpossibleConstraint[T <: HasUnit](a: Constraint[T], b: Constraint[T]) extends AggregateConstraint[T] { - - def and(that: Constraint[T]) = this +sealed abstract class AggregateConstraint extends Constraint +final case class ImpossibleConstraint(a: Constraint, b: Constraint) extends AggregateConstraint { + def and(that: Constraint) = this } -final case class AndConstraint[T <: HasUnit](constraints: Seq[Constraint[T]]) extends AggregateConstraint[T] { - - def and(that: Constraint[T]) = that match { - case that: ImpossibleConstraint[T] => that - case that: Unconstrained[T] => this +final case class AndConstraint(constraints: Seq[Constraint]) extends AggregateConstraint { + def and(that: Constraint) = that match { + case that: ImpossibleConstraint => that + case that: Unconstrained => this case that => AndConstraint(this, that) } - def flatten: AndConstraint[T] = { + def flatten: AndConstraint = { AndConstraint(constraints.collect({ - case x: AndConstraint[T] => x.flatten.constraints - case x: PrimitiveConstraint[T] => Seq(x) + case x: AndConstraint => x.flatten.constraints + case x: PrimitiveConstraint => Seq(x) }).reduce(_ ++ _)) } - def reduce: Constraint[T] = { + def reduce: Constraint = { val flat = this.flatten.constraints val exact = flat.collect({ - case x:EqualTo[T] => x - }).reduceOption[Constraint[T]](_.and(_)) + case x:EqualTo => x + }).reduceOption[Constraint](_.and(_)) val gt = flat.collect({ - case x:GreaterThanOrEqualTo[T] => x - }).reduceOption[Constraint[T]](_.and(_)) + case x:GreaterThanOrEqualTo => x + }).reduceOption[Constraint](_.and(_)) val lt = flat.collect({ - case x:LessThanOrEqualTo[T] => x - }).reduceOption[Constraint[T]](_.and(_)) + case x:LessThanOrEqualTo => x + }).reduceOption[Constraint](_.and(_)) val mult = flat.collect({ - case x:MultipleOf[T] => x - }).reduceOption[Constraint[T]](_.and(_)) + case x:MultipleOf => x + }).reduceOption[Constraint](_.and(_)) // if exact is defined, we'll either be exact or impossible exact.map({ value => @@ -419,13 +114,10 @@ final case class AndConstraint[T <: HasUnit](constraints: Seq[Constraint[T]]) ex ??? } } - } object AndConstraint { - - def apply[T <: HasUnit](a: Constraint[T], b: Constraint[T]): AndConstraint[T] = AndConstraint(Seq(a, b)) - + def apply(a: Constraint, b: Constraint): AndConstraint = AndConstraint(Seq(a, b)) } sealed trait PlacementAnchor @@ -450,6 +142,3 @@ object UpperLeft { def apply() = new UpperLeft } object UpperMiddle { def apply() = new UpperMiddle } object UpperRight { def apply() = new UpperRight } -final case class RatioPlacementConstraint(x: Constraint[Rational], y: Constraint[Rational], anchor: PlacementAnchor = LowerLeft()) -final case class LengthPlacementConstraint(x: Constraint[LengthUnit], y: Constraint[LengthUnit], anchor: PlacementAnchor = LowerLeft()) - diff --git a/floorplan/src/main/scala/barstools/floorplan/FloorplanSerialization.scala b/floorplan/src/main/scala/barstools/floorplan/FloorplanSerialization.scala index a2daa449..035356c6 100644 --- a/floorplan/src/main/scala/barstools/floorplan/FloorplanSerialization.scala +++ b/floorplan/src/main/scala/barstools/floorplan/FloorplanSerialization.scala @@ -12,9 +12,7 @@ object FloorplanSerialization { typeOf[Element], typeOf[Unit], typeOf[PlacementAnchor], - typeOf[Constraint[Rational]], - typeOf[Constraint[LengthUnit]], - typeOf[Constraint[AreaUnit]], + typeOf[Constraint], ).map(_.typeSymbol.asClass.knownDirectSubclasses.toList).reduce(_ ++ _) val formats = (new DefaultFormats { diff --git a/floorplan/src/main/scala/barstools/floorplan/IR.scala b/floorplan/src/main/scala/barstools/floorplan/IR.scala index 6eaeb665..3a0dce69 100644 --- a/floorplan/src/main/scala/barstools/floorplan/IR.scala +++ b/floorplan/src/main/scala/barstools/floorplan/IR.scala @@ -1,6 +1,8 @@ // See LICENSE for license details package barstools.floorplan +import scala.math.{BigInt, BigDecimal} + // TODO Make some of this stuff private // TODO make level an enum // TODO add versioning to the FPIR file @@ -31,22 +33,22 @@ private[floorplan] final case class HierarchicalBarrier( ////////////////////////////////////////////// Rectangular things trait ConstrainedRectLike { - def width: Constraint[LengthUnit] - def height: Constraint[LengthUnit] - def area: Constraint[AreaUnit] - def aspectRatio: Constraint[Rational] + def width: Constraint + def height: Constraint + def area: Constraint + def aspectRatio: Constraint } trait SizedRectLike { - def width: LengthUnit - def height: LengthUnit + def width: BigDecimal + def height: BigDecimal } trait PlacedRectLike { - def x: LengthUnit - def y: LengthUnit - def width: LengthUnit - def height: LengthUnit + def x: BigDecimal + def y: BigDecimal + def width: BigDecimal + def height: BigDecimal } object IRLevel { @@ -71,44 +73,44 @@ sealed abstract class PlacedRectPrimitive extends Primitive with PlacedRectLike private[floorplan] final case class ConstrainedSpacerRect( name: String, - width: Constraint[LengthUnit] = Unconstrained[LengthUnit], - height: Constraint[LengthUnit] = Unconstrained[LengthUnit], - area: Constraint[AreaUnit] = Unconstrained[AreaUnit], - aspectRatio: Constraint[Rational] = Unconstrained[Rational] + width: Constraint = Unconstrained(), + height: Constraint = Unconstrained(), + area: Constraint = Unconstrained(), + aspectRatio: Constraint = Unconstrained() ) extends ConstrainedRectPrimitive private[floorplan] final case class SizedSpacerRect( name: String, - x: LengthUnit, - y: LengthUnit, - width: LengthUnit, - height: LengthUnit + x: BigDecimal, + y: BigDecimal, + width: BigDecimal, + height: BigDecimal ) extends SizedRectPrimitive // No PlacedSpacerRect exists because they're only for spacing private[floorplan] final case class ConstrainedLogicRect( name: String, - width: Constraint[LengthUnit], - height: Constraint[LengthUnit], - area: Constraint[AreaUnit], - aspectRatio: Constraint[Rational], + width: Constraint, + height: Constraint, + area: Constraint, + aspectRatio: Constraint, hardBoundary: Boolean ) extends ConstrainedRectPrimitive private[floorplan] final case class SizedLogicRect( name: String, - width: LengthUnit, - height: LengthUnit, + width: BigDecimal, + height: BigDecimal, hardBoundary: Boolean ) extends SizedRectPrimitive private[floorplan] final case class PlacedLogicRect( name: String, - x: LengthUnit, - y: LengthUnit, - width: LengthUnit, - height: LengthUnit, + x: BigDecimal, + y: BigDecimal, + width: BigDecimal, + height: BigDecimal, hardBoundary: Boolean ) extends PlacedRectPrimitive @@ -130,7 +132,7 @@ private[floorplan] final case class WeightedGrid( xDim: Int, yDim: Int, elements: Seq[Option[String]], - weights: Seq[Rational], + weights: Seq[BigDecimal], packed: Boolean ) extends Grid { def level = 2 @@ -148,10 +150,10 @@ private[floorplan] final case class MemElement( private[floorplan] final case class MemElementArray( name: String, elements: Seq[Option[String]], - width: Constraint[LengthUnit] = Unconstrained[LengthUnit], - height: Constraint[LengthUnit] = Unconstrained[LengthUnit], - area: Constraint[AreaUnit] = Unconstrained[AreaUnit], - aspectRatio: Constraint[Rational] = Unconstrained[Rational] + width: Constraint = Unconstrained(), + height: Constraint = Unconstrained(), + area: Constraint = Unconstrained(), + aspectRatio: Constraint = Unconstrained() ) extends Group with ConstrainedRectLike { def level = 3 } @@ -163,10 +165,10 @@ private[floorplan] final case class MemElementArray( private[floorplan] final case class MemMacroArray( name: String, elements: Seq[Option[String]], - width: Constraint[LengthUnit] = Unconstrained[LengthUnit], - height: Constraint[LengthUnit] = Unconstrained[LengthUnit], - area: Constraint[AreaUnit] = Unconstrained[AreaUnit], - aspectRatio: Constraint[Rational] = Unconstrained[Rational] + width: Constraint = Unconstrained(), + height: Constraint = Unconstrained(), + area: Constraint = Unconstrained(), + aspectRatio: Constraint = Unconstrained() ) extends Group with ConstrainedRectLike { def level = 2 } @@ -182,17 +184,17 @@ private[floorplan] final case class AbstractMacro ( private[floorplan] final case class SizedMacro ( name: String, ofModule: String, - width: LengthUnit, - height: LengthUnit + width: BigDecimal, + height: BigDecimal ) extends SizedRectPrimitive // Reference to a macro blackbox that has known dimensions and has been placed private[floorplan] final case class PlacedMacro ( name: String, ofModule: String, - x: LengthUnit, - y: LengthUnit, - width: LengthUnit, - height: LengthUnit + x: BigDecimal, + y: BigDecimal, + width: BigDecimal, + height: BigDecimal ) extends PlacedRectPrimitive diff --git a/floorplan/src/main/scala/barstools/floorplan/chisel/API.scala b/floorplan/src/main/scala/barstools/floorplan/chisel/API.scala index c4bacabc..5e1f53a3 100644 --- a/floorplan/src/main/scala/barstools/floorplan/chisel/API.scala +++ b/floorplan/src/main/scala/barstools/floorplan/chisel/API.scala @@ -8,6 +8,7 @@ import firrtl.annotations.{ReferenceTarget, InstanceTarget, ModuleTarget, Target import barstools.floorplan._ import barstools.floorplan.firrtl.{FloorplanAnnotation, MemFloorplanAnnotation, InstanceFloorplanAnnotation, NoReferenceFloorplanAnnotation} import scala.collection.mutable.{ArraySeq, ArrayBuffer, HashMap, Set, HashSet} +import scala.math.{BigInt, BigDecimal} final case class ChiselFloorplanException(message: String) extends Exception(message: String) @@ -26,10 +27,10 @@ final class ChiselFloorplanContext private[chisel] (val root: Target, topElement def elements: Seq[ChiselElement] = elementBuf.toSeq def createRect[T <: RawModule](module: T, - width: Constraint[LengthUnit] = Unconstrained[LengthUnit], - height: Constraint[LengthUnit] = Unconstrained[LengthUnit], - area: Constraint[AreaUnit] = Unconstrained[AreaUnit], - aspectRatio: Constraint[Rational] = Unconstrained[Rational], + width: Constraint = Unconstrained(), + height: Constraint = Unconstrained(), + area: Constraint = Unconstrained(), + aspectRatio: Constraint = Unconstrained(), hardBoundary: Boolean = true ): ChiselLogicRect = { val inst: Target = module.toAbsoluteTarget @@ -40,10 +41,10 @@ final class ChiselFloorplanContext private[chisel] (val root: Target, topElement def createSpacer( name: Option[String] = None, - width: Constraint[LengthUnit] = Unconstrained[LengthUnit], - height: Constraint[LengthUnit] = Unconstrained[LengthUnit], - area: Constraint[AreaUnit] = Unconstrained[AreaUnit], - aspectRatio: Constraint[Rational] = Unconstrained[Rational] + width: Constraint = Unconstrained(), + height: Constraint = Unconstrained(), + area: Constraint = Unconstrained(), + aspectRatio: Constraint = Unconstrained() ): ChiselSpacerRect = { val nameStr = FloorplanDatabase.getUnusedName(root, name) val elt = new ChiselSpacerRect(root, nameStr, width, height, area, aspectRatio) @@ -75,10 +76,10 @@ final class ChiselFloorplanContext private[chisel] (val root: Target, topElement object Floorplan { def apply[T <: RawModule](module: T, - width: Constraint[LengthUnit] = Unconstrained[LengthUnit], - height: Constraint[LengthUnit] = Unconstrained[LengthUnit], - area: Constraint[AreaUnit] = Unconstrained[AreaUnit], - aspectRatio: Constraint[Rational] = Unconstrained[Rational], + width: Constraint = Unconstrained(), + height: Constraint = Unconstrained(), + area: Constraint = Unconstrained(), + aspectRatio: Constraint = Unconstrained(), hardBoundary: Boolean = true ) = { val root: Target = module.toAbsoluteTarget @@ -165,17 +166,17 @@ object FloorplanUnits { def get = unit - def area(x: Double): AreaUnit = { + def area(x: Double): BigDecimal = { unit.map { u => - AreaUnit(scala.math.round(u*x*x)) + BigDecimal(scala.math.round(u*x*x)) }.getOrElse { throw new FloorplanUnitsException("Cannot create floorplan with concrete units- units have not been set! Call FloorplanUnits.set first.") } } - def length(x: Double): LengthUnit = { + def length(x: Double): BigDecimal = { unit.map { u => - LengthUnit(scala.math.round(u*x)) + BigDecimal(scala.math.round(u*x)) }.getOrElse { throw new FloorplanUnitsException("Cannot create floorplan with concrete units- units have not been set! Call FloorplanUnits.set first.") } @@ -241,10 +242,10 @@ final class ChiselLogicRect private[chisel] ( root: Target, name: String, instance: Target, - val width: Constraint[LengthUnit], - val height: Constraint[LengthUnit], - val area: Constraint[AreaUnit], - val aspectRatio: Constraint[Rational], + val width: Constraint, + val height: Constraint, + val area: Constraint, + val aspectRatio: Constraint, val hardBoundary: Boolean ) extends ChiselInstanceElement(root, name, instance) { protected def generateElement(): Element = ConstrainedLogicRect(name, width, height, area, aspectRatio, hardBoundary) @@ -253,10 +254,10 @@ final class ChiselLogicRect private[chisel] ( final class ChiselSpacerRect private[chisel] ( root: Target, name: String, - val width: Constraint[LengthUnit] = Unconstrained[LengthUnit], - val height: Constraint[LengthUnit] = Unconstrained[LengthUnit], - val area: Constraint[AreaUnit] = Unconstrained[AreaUnit], - val aspectRatio: Constraint[Rational] = Unconstrained[Rational] + val width: Constraint = Unconstrained(), + val height: Constraint = Unconstrained(), + val area: Constraint = Unconstrained(), + val aspectRatio: Constraint = Unconstrained() ) extends ChiselSpacerElement(root, name) { protected def generateElement(): Element = ConstrainedSpacerRect(name, width, height, area, aspectRatio) } @@ -294,13 +295,13 @@ final class ChiselWeightedGrid private[chisel] ( assert(yDim > 0) protected val elements = ArraySeq.fill[Option[ChiselElement]](xDim*yDim) { Option.empty[ChiselElement] } - private val weights = ArraySeq.fill[Rational](xDim*yDim) { Rational(1) } + private val weights = ArraySeq.fill[BigDecimal](xDim*yDim) { BigDecimal(1) } private var _isCommitted = false def isCommitted = _isCommitted - def set(x: Int, y: Int, element: ChiselElement, weight: Rational): Unit = { + def set(x: Int, y: Int, element: ChiselElement, weight: BigDecimal): Unit = { if (isCommitted) throw new ChiselFloorplanException("Cannot modify a ChiselWeightedGrid after committing") if (x >= xDim) throw new IndexOutOfBoundsException(s"X-value ${x} >= ${xDim} in ChiselWeightedGrid") if (y >= yDim) throw new IndexOutOfBoundsException(s"Y-value ${y} >= ${yDim} in ChiselWeightedGrid") @@ -309,15 +310,15 @@ final class ChiselWeightedGrid private[chisel] ( weights(y*xDim + x) = weight } - def set(x: Int, y: Int, element: ChiselElement): Unit = set(x, y, element, Rational(1)) + def set(x: Int, y: Int, element: ChiselElement): Unit = set(x, y, element, BigDecimal(1)) - def set(x: Int, y: Int, eltModule: RawModule, weight: Rational): Unit = { + def set(x: Int, y: Int, eltModule: RawModule, weight: BigDecimal): Unit = { // TODO what should the hardness of this boundary be? - val element = new ChiselLogicRect(eltModule, Unconstrained[LengthUnit], Unconstrained[LengthUnit], Unconstrained[AreaUnit], Unconstrained[Rational], true) + val element = new ChiselLogicRect(eltModule, Unconstrained(), Unconstrained(), Unconstrained(), Unconstrained(), true) set(x, y, element, weight) } - def set(x: Int, y: Int, root: Target): Unit = set(x, y, module, Rational(1)) + def set(x: Int, y: Int, root: Target): Unit = set(x, y, module, BigDecimal(1)) protected def generateElement(): Element = { _isCommitted = true diff --git a/floorplan/src/main/scala/barstools/floorplan/hammer/HammerIR.scala b/floorplan/src/main/scala/barstools/floorplan/hammer/HammerIR.scala index e7a1dffe..16905d20 100644 --- a/floorplan/src/main/scala/barstools/floorplan/hammer/HammerIR.scala +++ b/floorplan/src/main/scala/barstools/floorplan/hammer/HammerIR.scala @@ -4,6 +4,7 @@ package barstools.floorplan.hammer import barstools.floorplan._ import java.io.{Writer} +import scala.math.{BigInt, BigDecimal} // This implements a small subset of HammerIR @@ -56,7 +57,7 @@ object HammerIR { def deserialize(s: String): HammerIR = HammerSerialization.deserialize(s) - def toMicrons(l: LengthUnit): Double = { + def toMicrons(l: BigDecimal): Double = { 0.0 // TODO } } diff --git a/floorplan/src/main/scala/barstools/floorplan/package.scala b/floorplan/src/main/scala/barstools/floorplan/package.scala new file mode 100644 index 00000000..b5d540da --- /dev/null +++ b/floorplan/src/main/scala/barstools/floorplan/package.scala @@ -0,0 +1,16 @@ +// See LICENSE for license details +package barstools +package object floorplan { + +import scala.math.{BigInt, BigDecimal} +import scala.collection.Seq + +def gcd(a: BigDecimal, b: BigDecimal): BigDecimal = { + val scaleFactor = 1 << Seq(a.scale, b.scale).max + val scaledGCD = (a*scaleFactor).toBigInt.gcd((b*scaleFactor).toBigInt) + BigDecimal(scaledGCD, scaleFactor) +} + +def lcm(a: BigDecimal, b: BigDecimal): BigDecimal = (a*b)/gcd(a,b) + +} From f7a62a218acc2c7885acdb962e1ff8e078d7ff5a Mon Sep 17 00:00:00 2001 From: John Wright Date: Sat, 17 Jul 2021 22:26:15 -0700 Subject: [PATCH 37/73] Add ofModule to hierarchical elements --- .../main/scala/barstools/floorplan/IR.scala | 35 ++++++++++++++++- .../barstools/floorplan/chisel/API.scala | 33 ++++++++++++---- .../barstools/floorplan/hammer/HammerIR.scala | 38 ++++++++++++++----- 3 files changed, 89 insertions(+), 17 deletions(-) diff --git a/floorplan/src/main/scala/barstools/floorplan/IR.scala b/floorplan/src/main/scala/barstools/floorplan/IR.scala index 3a0dce69..61b17a0a 100644 --- a/floorplan/src/main/scala/barstools/floorplan/IR.scala +++ b/floorplan/src/main/scala/barstools/floorplan/IR.scala @@ -25,7 +25,8 @@ sealed abstract class Group extends Element { ////////////////////////////////////////////// Hierarchical barrier private[floorplan] final case class HierarchicalBarrier( - name: String + name: String, + ofModule: String ) extends Primitive { final def level = 0 } @@ -89,6 +90,14 @@ private[floorplan] final case class SizedSpacerRect( // No PlacedSpacerRect exists because they're only for spacing +/////////////////////////// Hierarchical top modules + +case class Margins(left: BigDecimal, right: BigDecimal, top: BigDecimal, bottom: BigDecimal) + +object Margins { + def empty = Margins(BigDecimal(0), BigDecimal(0), BigDecimal(0), BigDecimal(0)) +} + private[floorplan] final case class ConstrainedLogicRect( name: String, width: Constraint, @@ -114,6 +123,30 @@ private[floorplan] final case class PlacedLogicRect( hardBoundary: Boolean ) extends PlacedRectPrimitive + +private[floorplan] final case class ConstrainedHierarchicalTop( + name: String, + ofModule: String, + width: Constraint, + height: Constraint, + area: Constraint, + aspectRatio: Constraint, + margins: Margins, + hardBoundary: Boolean +) extends ConstrainedRectPrimitive + +private[floorplan] final case class PlacedHierarchicalTop( + name: String, + ofModule: String, + width: BigDecimal, + height: BigDecimal, + margins: Margins, + hardBoundary: Boolean +) extends PlacedRectPrimitive { + def x = BigDecimal(0) + def y = BigDecimal(0) +} + ////////////////////////////////////////////// Aggregate (Group) things sealed abstract class Grid extends Group { diff --git a/floorplan/src/main/scala/barstools/floorplan/chisel/API.scala b/floorplan/src/main/scala/barstools/floorplan/chisel/API.scala index 5e1f53a3..077d8d27 100644 --- a/floorplan/src/main/scala/barstools/floorplan/chisel/API.scala +++ b/floorplan/src/main/scala/barstools/floorplan/chisel/API.scala @@ -58,7 +58,7 @@ final class ChiselFloorplanContext private[chisel] (val root: Target, topElement } def addHier[T <: RawModule](module: T): ChiselHierarchicalBarrier = { - val inst: Target = module.toAbsoluteTarget + val inst = module.toAbsoluteTarget.asInstanceOf[InstanceTarget] val name = FloorplanDatabase.getUnusedName(root, inst) val elt = new ChiselHierarchicalBarrier(root, name, inst) addElement(elt) @@ -80,7 +80,8 @@ object Floorplan { height: Constraint = Unconstrained(), area: Constraint = Unconstrained(), aspectRatio: Constraint = Unconstrained(), - hardBoundary: Boolean = true + hardBoundary: Boolean = true, + margins: Margins = Margins.empty ) = { val root: Target = module.toAbsoluteTarget val modName = root match { @@ -89,7 +90,7 @@ object Floorplan { case _ => ??? } val name = FloorplanDatabase.getUnusedName(root, modName) - val elt = new ChiselLogicRect(root, name, root, width, height, area, aspectRatio, hardBoundary) + val elt = new ChiselHierarchicalTop(root, name, width, height, area, aspectRatio, margins, hardBoundary) FloorplanDatabase.register(root, elt) new ChiselFloorplanContext(root, elt) } @@ -190,7 +191,7 @@ sealed abstract class ChiselElement(val root: Target, val name: String) { def getAnnotations(): Seq[Annotation] = getFloorplanAnnotations() } -sealed abstract class ChiselSpacerElement(root: Target, name: String) extends ChiselElement(root, name) { +sealed abstract class ChiselNoReferenceElement(root: Target, name: String) extends ChiselElement(root, name) { private[chisel] def getFloorplanAnnotations() = Seq(NoReferenceFloorplanAnnotation(root, generateElement())) } @@ -230,12 +231,30 @@ sealed abstract class ChiselGroupElement(root: Target, name: String, val context private[chisel] def getFloorplanAnnotations() = Seq(NoReferenceFloorplanAnnotation(root, generateElement())) } +final class ChiselHierarchicalTop private[chisel] ( + root: Target, + name: String, + val width: Constraint, + val height: Constraint, + val area: Constraint, + val aspectRatio: Constraint, + val margins: Margins, + val hardBoundary: Boolean +) extends ChiselNoReferenceElement(root, name) { + val ofModule = root match { + case t: InstanceTarget => t.ofModule + case t: ModuleTarget => t.module + case _ => ??? + } + protected def generateElement(): Element = ConstrainedHierarchicalTop(name, ofModule, width, height, area, aspectRatio, margins, hardBoundary) +} + final class ChiselHierarchicalBarrier private[chisel] ( root: Target, name: String, - instance: Target + instance: InstanceTarget ) extends ChiselInstanceElement(root, name, instance) { - protected def generateElement(): Element = HierarchicalBarrier(name) + protected def generateElement(): Element = HierarchicalBarrier(name, instance.ofModule) } final class ChiselLogicRect private[chisel] ( @@ -258,7 +277,7 @@ final class ChiselSpacerRect private[chisel] ( val height: Constraint = Unconstrained(), val area: Constraint = Unconstrained(), val aspectRatio: Constraint = Unconstrained() -) extends ChiselSpacerElement(root, name) { +) extends ChiselNoReferenceElement(root, name) { protected def generateElement(): Element = ConstrainedSpacerRect(name, width, height, area, aspectRatio) } diff --git a/floorplan/src/main/scala/barstools/floorplan/hammer/HammerIR.scala b/floorplan/src/main/scala/barstools/floorplan/hammer/HammerIR.scala index 16905d20..a28b3ef1 100644 --- a/floorplan/src/main/scala/barstools/floorplan/hammer/HammerIR.scala +++ b/floorplan/src/main/scala/barstools/floorplan/hammer/HammerIR.scala @@ -25,16 +25,15 @@ object HammerIR { width = toMicrons(c.width), height = toMicrons(c.height), master = Some(c.ofModule), - margins = Some(PlacementMargins(0.0,0.0,0.0,0.0)), // TODO represent this in FPIR + margins = None, top_layer = None, // TODO need this for macros layers = None, // TODO need this for macros obs_types = None // TODO need this for macros )) case c: PlacedLogicRect => - val isTop = (record.root.split("/").length == 1) && (record.inst.get.split("/").length == 1) Some(PlacementConstraint( path = record.fullPath, - typ = if (isTop) PlacementType.toplevel else PlacementType.placement, + typ = PlacementType.placement, orientation = Orientation.r0, // TODO represent this in FPIR x = toMicrons(c.x), y = toMicrons(c.y), @@ -42,7 +41,23 @@ object HammerIR { width = toMicrons(c.width), height = toMicrons(c.height), master = None, - margins = if (isTop) Some(PlacementMargins(0.0,0.0,0.0,0.0)) else None, // TODO represent this in FPIR + margins = None, + top_layer = None, + layers = None, + obs_types = None + )) + case c: PlacedHierarchicalTop => + Some(PlacementConstraint( + path = record.fullPath, + typ = PlacementType.placement, + orientation = Orientation.r0, + x = toMicrons(c.x), + y = toMicrons(c.y), + create_physical = Some(false), + width = toMicrons(c.width), + height = toMicrons(c.height), + master = None, + margins = Some(PlacementMargins(c.margins)), top_layer = None, layers = None, obs_types = None @@ -54,12 +69,8 @@ object HammerIR { } def serialize(h: HammerIR): String = HammerSerialization.serialize(h) - def deserialize(s: String): HammerIR = HammerSerialization.deserialize(s) - - def toMicrons(l: BigDecimal): Double = { - 0.0 // TODO - } + def toMicrons(l: BigDecimal): Double = l.toDouble } final case class HammerIR private[hammer] ( @@ -104,3 +115,12 @@ final case class PlacementMargins private[hammer] ( bottom: Double ) +object PlacementMargins { + def apply(m: barstools.floorplan.Margins): PlacementMargins = PlacementMargins( + left=m.left.toDouble, + right=m.right.toDouble, + top=m.top.toDouble, + bottom=m.bottom.toDouble + ) +} + From e5e0612e8f7308159b899536101d970bffc02e93 Mon Sep 17 00:00:00 2001 From: John Wright Date: Sun, 18 Jul 2021 00:00:37 -0700 Subject: [PATCH 38/73] Add grid elements --- .../main/scala/barstools/floorplan/IR.scala | 11 +- .../barstools/floorplan/chisel/API.scala | 123 ++++++++++-------- 2 files changed, 78 insertions(+), 56 deletions(-) diff --git a/floorplan/src/main/scala/barstools/floorplan/IR.scala b/floorplan/src/main/scala/barstools/floorplan/IR.scala index 61b17a0a..b69ece1c 100644 --- a/floorplan/src/main/scala/barstools/floorplan/IR.scala +++ b/floorplan/src/main/scala/barstools/floorplan/IR.scala @@ -168,7 +168,16 @@ private[floorplan] final case class WeightedGrid( weights: Seq[BigDecimal], packed: Boolean ) extends Grid { - def level = 2 + def level = 1 +} + +private[floorplan] final case class ElasticGrid( + name: String, + xDim: Int, + yDim: Int, + elements: Seq[Option[String]] +) extends Grid { + def level = 1 } diff --git a/floorplan/src/main/scala/barstools/floorplan/chisel/API.scala b/floorplan/src/main/scala/barstools/floorplan/chisel/API.scala index 077d8d27..00a1901b 100644 --- a/floorplan/src/main/scala/barstools/floorplan/chisel/API.scala +++ b/floorplan/src/main/scala/barstools/floorplan/chisel/API.scala @@ -51,6 +51,27 @@ final class ChiselFloorplanContext private[chisel] (val root: Target, topElement addElement(elt) } + def createElasticGrid(xDim: Int, yDim: Int, name: Option[String] = None): ChiselElasticGrid = { + val nameStr = FloorplanDatabase.getUnusedName(root, name) + val elt = new ChiselElasticGrid(root, nameStr, this, xDim, yDim) + addElement(elt) + } + + def createElasticArray(dim: Int, dir: Direction, name: Option[String]): ChiselElasticArray = { + val nameStr = FloorplanDatabase.getUnusedName(root, name) + val elt = new ChiselElasticArray(root, nameStr, this, dim, dir) + addElement(elt) + } + + def createElasticArray(dim: Int, name: Option[String]): ChiselElasticArray = createElasticArray(dim, Direction.Horizontal, name) + def createElasticArray(dim: Int, dir: Direction): ChiselElasticArray = createElasticArray(dim, dir, None) + def createElasticArray(dim: Int): ChiselElasticArray = createElasticArray(dim, Direction.Horizontal, None) + def createElasticArray(elts: Seq[ChiselElement], dir: Direction = Direction.Horizontal, name: Option[String] = None): ChiselElasticArray = { + val ary = createElasticArray(elts.length, dir, name) + elts.zipWithIndex.foreach { case (e, i) => ary.placeElementAt(e, i) } + ary + } + def createMemArray(name: Option[String] = None): ChiselMemArray = { val nameStr = FloorplanDatabase.getUnusedName(root, name) val elt = new ChiselMemArray(root, nameStr, this) @@ -185,6 +206,19 @@ object FloorplanUnits { } +trait Direction { + def ifH[T](h: T, v: T): T = this match { + case Direction.Horizontal => h + case Direction.Vertical => v + } + def ifV[T](h: T, v: T): T = ifH(v, h) +} + +object Direction { + case object Vertical extends Direction + case object Horizontal extends Direction +} + sealed abstract class ChiselElement(val root: Target, val name: String) { protected def generateElement(): Element private[chisel] def getFloorplanAnnotations(): Seq[FloorplanAnnotation] @@ -205,8 +239,8 @@ sealed abstract class ChiselMemElement(root: Target, name: String, val reference sealed abstract class ChiselGroupElement(root: Target, name: String, val context: ChiselFloorplanContext) extends ChiselElement(root, name) { protected def initialSize: Int - private val elements = Seq.fill(initialSize)(Option.empty[ChiselElement]).toBuffer - private var isCommitted = false + protected val elements = Seq.fill(initialSize)(Option.empty[ChiselElement]).toBuffer + protected var isCommitted = false protected def generateGroupElement(names: Seq[Option[String]]): Group @@ -215,20 +249,34 @@ sealed abstract class ChiselGroupElement(root: Target, name: String, val context generateGroupElement(elements.map(_.map(_.name))) } - private[chisel] def placeElementAt(e: ChiselElement, idx: Int) { + private[chisel] def _placeElementAt(e: ChiselElement, idx: Int) { assert(!isCommitted, "Cannot add elements after committing") // This is only supported in scala 2.13 //elements.padToInPlace(idx+1, None) for (i <- elements.length until (idx+1)) elements += None + assert(!elements(idx).isDefined, "Already added at this location") elements(idx) = Some(e) } + private[chisel] def getFloorplanAnnotations() = Seq(NoReferenceFloorplanAnnotation(root, generateElement())) +} + +abstract class ChiselArrayElement(root: Target, name: String, context: ChiselFloorplanContext) extends ChiselGroupElement(root, name, context) { + private[chisel] def addElement(e: ChiselElement) { assert(!isCommitted, "Cannot add elements after committing") elements += Some(e) } - private[chisel] def getFloorplanAnnotations() = Seq(NoReferenceFloorplanAnnotation(root, generateElement())) +} + +abstract class ChiselGridElement(root: Target, name: String, context: ChiselFloorplanContext, val xDim: Int, val yDim: Int) extends ChiselGroupElement(root, name, context) { + + protected def initialSize = xDim * yDim + protected def toIdx(x: Int, y: Int): Int = xDim*y + x + + def placeElementAt(e: ChiselElement, x: Int, y: Int) { _placeElementAt(e, toIdx(x, y)) } + } final class ChiselHierarchicalTop private[chisel] ( @@ -293,64 +341,29 @@ final class ChiselMemArray private[chisel] ( root: Target, name: String, context: ChiselFloorplanContext -) extends ChiselGroupElement(root, name, context) { +) extends ChiselArrayElement(root, name, context) { protected def initialSize = 0 protected def generateGroupElement(names: Seq[Option[String]]): Group = MemElementArray(name, names) def addMem[T <: Data](mem: MemBase[T]) = this.addElement(this.context.addMem(mem)) } - -/* -final class ChiselWeightedGrid private[chisel] ( +class ChiselElasticGrid private[chisel] ( root: Target, name: String, - val xDim: Int, - val yDim: Int, - val packed: Boolean -) extends ChiselGroupElement(module, name) { - - assert(xDim > 0) - assert(yDim > 0) - - protected val elements = ArraySeq.fill[Option[ChiselElement]](xDim*yDim) { Option.empty[ChiselElement] } - private val weights = ArraySeq.fill[BigDecimal](xDim*yDim) { BigDecimal(1) } - - private var _isCommitted = false - - def isCommitted = _isCommitted - - def set(x: Int, y: Int, element: ChiselElement, weight: BigDecimal): Unit = { - if (isCommitted) throw new ChiselFloorplanException("Cannot modify a ChiselWeightedGrid after committing") - if (x >= xDim) throw new IndexOutOfBoundsException(s"X-value ${x} >= ${xDim} in ChiselWeightedGrid") - if (y >= yDim) throw new IndexOutOfBoundsException(s"Y-value ${y} >= ${yDim} in ChiselWeightedGrid") - if (elements(y*xDim + x).isDefined) throw new ChiselFloorplanException(s"Coordinates (${x},${y}) already in use") - elements(y*xDim + x) = Some(element) - weights(y*xDim + x) = weight - } - - def set(x: Int, y: Int, element: ChiselElement): Unit = set(x, y, element, BigDecimal(1)) - - def set(x: Int, y: Int, eltModule: RawModule, weight: BigDecimal): Unit = { - // TODO what should the hardness of this boundary be? - val element = new ChiselLogicRect(eltModule, Unconstrained(), Unconstrained(), Unconstrained(), Unconstrained(), true) - set(x, y, element, weight) - } - - def set(x: Int, y: Int, root: Target): Unit = set(x, y, module, BigDecimal(1)) - - protected def generateElement(): Element = { - _isCommitted = true - elements.transform(_.orElse(Some(Floorplan.dummy(module)))) - WeightedGrid( - name, - xDim, - yDim, - elements.map(_.get.name), - weights, - packed) - } - + context: ChiselFloorplanContext, + xDim: Int, + yDim: Int +) extends ChiselGridElement(root, name, context, xDim, yDim) { + final protected def generateGroupElement(names: Seq[Option[String]]): Group = ElasticGrid(name, xDim, yDim, names) } -*/ +class ChiselElasticArray private[chisel] ( + root: Target, + name: String, + context: ChiselFloorplanContext, + dim: Int, + val dir: Direction +) extends ChiselElasticGrid(root, name, context, dir.ifH(dim,1), dir.ifV(dim,1)) { + def placeElementAt(e: ChiselElement, i: Int) { placeElementAt(e, dir.ifH(i,0), dir.ifV(0,i)) } +} From 5eb084e3da882ccba70d77a6459174b108732a1c Mon Sep 17 00:00:00 2001 From: John Wright Date: Sun, 18 Jul 2021 02:26:28 -0700 Subject: [PATCH 39/73] Get sideband annotations working --- .../main/scala/barstools/floorplan/IR.scala | 11 ++- .../barstools/floorplan/chisel/API.scala | 6 +- .../compiler/FloorplanCompiler.scala | 7 ++ .../barstools/floorplan/compiler/Pass.scala | 8 +- .../compiler/SidebandAnnotation.scala | 44 +++++++++- .../compiler/SidebandAnnotationPass.scala | 81 +++++++++++++++++++ .../SidebandAnnotationSerialization.scala | 19 +++++ .../compiler/TransformMemsPass.scala | 2 +- 8 files changed, 163 insertions(+), 15 deletions(-) create mode 100644 floorplan/src/main/scala/barstools/floorplan/compiler/SidebandAnnotationPass.scala create mode 100644 floorplan/src/main/scala/barstools/floorplan/compiler/SidebandAnnotationSerialization.scala diff --git a/floorplan/src/main/scala/barstools/floorplan/IR.scala b/floorplan/src/main/scala/barstools/floorplan/IR.scala index b69ece1c..d2048052 100644 --- a/floorplan/src/main/scala/barstools/floorplan/IR.scala +++ b/floorplan/src/main/scala/barstools/floorplan/IR.scala @@ -33,19 +33,19 @@ private[floorplan] final case class HierarchicalBarrier( ////////////////////////////////////////////// Rectangular things -trait ConstrainedRectLike { +sealed trait ConstrainedRectLike { def width: Constraint def height: Constraint def area: Constraint def aspectRatio: Constraint } -trait SizedRectLike { +sealed trait SizedRectLike { def width: BigDecimal def height: BigDecimal } -trait PlacedRectLike { +sealed trait PlacedRectLike { def x: BigDecimal def y: BigDecimal def width: BigDecimal @@ -100,6 +100,7 @@ object Margins { private[floorplan] final case class ConstrainedLogicRect( name: String, + ofModule: String, width: Constraint, height: Constraint, area: Constraint, @@ -109,6 +110,7 @@ private[floorplan] final case class ConstrainedLogicRect( private[floorplan] final case class SizedLogicRect( name: String, + ofModule: String, width: BigDecimal, height: BigDecimal, hardBoundary: Boolean @@ -116,6 +118,7 @@ private[floorplan] final case class SizedLogicRect( private[floorplan] final case class PlacedLogicRect( name: String, + ofModule: String, x: BigDecimal, y: BigDecimal, width: BigDecimal, @@ -225,7 +228,7 @@ private[floorplan] final case class AbstractMacro ( // Reference to a macro blackbox that has known dimensions private[floorplan] final case class SizedMacro ( name: String, - ofModule: String, + ofModule: String, // TODO we should replace this with the OfModule in the Record width: BigDecimal, height: BigDecimal ) extends SizedRectPrimitive diff --git a/floorplan/src/main/scala/barstools/floorplan/chisel/API.scala b/floorplan/src/main/scala/barstools/floorplan/chisel/API.scala index 00a1901b..ec9c8f17 100644 --- a/floorplan/src/main/scala/barstools/floorplan/chisel/API.scala +++ b/floorplan/src/main/scala/barstools/floorplan/chisel/API.scala @@ -33,7 +33,7 @@ final class ChiselFloorplanContext private[chisel] (val root: Target, topElement aspectRatio: Constraint = Unconstrained(), hardBoundary: Boolean = true ): ChiselLogicRect = { - val inst: Target = module.toAbsoluteTarget + val inst = module.toAbsoluteTarget.asInstanceOf[InstanceTarget] val name = FloorplanDatabase.getUnusedName(root, inst) val elt = new ChiselLogicRect(root, name, inst, width, height, area, aspectRatio, hardBoundary) addElement(elt) @@ -308,14 +308,14 @@ final class ChiselHierarchicalBarrier private[chisel] ( final class ChiselLogicRect private[chisel] ( root: Target, name: String, - instance: Target, + instance: InstanceTarget, val width: Constraint, val height: Constraint, val area: Constraint, val aspectRatio: Constraint, val hardBoundary: Boolean ) extends ChiselInstanceElement(root, name, instance) { - protected def generateElement(): Element = ConstrainedLogicRect(name, width, height, area, aspectRatio, hardBoundary) + protected def generateElement(): Element = ConstrainedLogicRect(name, instance.ofModule, width, height, area, aspectRatio, hardBoundary) } final class ChiselSpacerRect private[chisel] ( diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanCompiler.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanCompiler.scala index 82db42d2..93cd17a7 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanCompiler.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanCompiler.scala @@ -7,6 +7,7 @@ case class FloorplanOptions( outFile: String = "", outFmt: OutputFormat = OutputFormat.HammerIR, inFiles: Seq[String] = Seq(), + sbAnnoFiles: Seq[String] = Seq(), memInstMapFiles: Seq[String] = Seq() ) @@ -32,6 +33,12 @@ object FloorplanCompiler extends App { action((x, c) => c.copy(memInstMapFiles = c.memInstMapFiles :+ x)). text("file containing the memory instance map") + opt[String]('b', "sideband-anno-file"). + required(). + valueName(""). + action((x, c) => c.copy(sbAnnoFiles = c.sbAnnoFiles :+ x)). + text("output file name") + opt[Unit]('f', "output-fpir"). action((x, c) => c.copy(outFmt = OutputFormat.FloorplanIR)). text("emit floorplanIR") diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/Pass.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/Pass.scala index 923b1d47..12276b48 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/Pass.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/Pass.scala @@ -7,10 +7,10 @@ object Pass { def all(opts: FloorplanOptions): Seq[Pass] = { val instMap = MemInstMap.fromFiles(opts.memInstMapFiles) - val sbAnnos = Seq() // TODO + val sbAnnos = SidebandAnnotationMap.fromFiles(opts.sbAnnoFiles) Seq( - new SidebandAnnotationPass(sbAnnos), new TransformMemsPass(instMap), + new SidebandAnnotationPass(sbAnnos), new TopDownPropagationPass, new BottomUpPropagationPass, new ResolveConstraintsPass @@ -22,10 +22,6 @@ abstract class Pass { def execute(state: FloorplanState): FloorplanState } -class SidebandAnnotationPass(annos: Seq[SidebandAnnotation]) extends Pass { - def execute(state: FloorplanState): FloorplanState = state // TODO -} - class TopDownPropagationPass extends Pass { def execute(state: FloorplanState): FloorplanState = state // TODO } diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/SidebandAnnotation.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/SidebandAnnotation.scala index 50fc9d3b..068eabc8 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/SidebandAnnotation.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/SidebandAnnotation.scala @@ -1,4 +1,46 @@ // See LICENSE for license details package barstools.floorplan.compiler -case class SidebandAnnotation(name: String) // TODO +import barstools.floorplan._ +import scala.collection.{Seq, Map} + +case class SidebandAnnotation( + ofModule: String, + width: Option[BigDecimal] = None, + height: Option[BigDecimal] = None, + area: Option[BigDecimal] = None +) { + def ++(that: SidebandAnnotation): SidebandAnnotation = { + assert(this.ofModule == that.ofModule) + assert(!this.width.isDefined || !that.width.isDefined) + assert(!this.height.isDefined || !that.height.isDefined) + assert(!this.area.isDefined || !that.area.isDefined) + SidebandAnnotation( + this.ofModule, + this.width.orElse(that.width), + this.height.orElse(that.width), + this.area.orElse(that.area) + ) + } + + def widthConstraint = width.map(x => EqualTo(x)).getOrElse(Unconstrained()) + def heightConstraint = height.map(x => EqualTo(x)).getOrElse(Unconstrained()) + def areaConstraint = area.map(x => EqualTo(x)).getOrElse(Unconstrained()) +} + +object SidebandAnnotationMap { + def fromFiles(files: Seq[String]): Map[String, SidebandAnnotation] = fromSeq(SidebandAnnotationSeq.fromFiles(files)) + def fromFile(file: String): Map[String, SidebandAnnotation] = fromSeq(SidebandAnnotationSeq.fromFile(file)) + def fromSeq(seq: Seq[SidebandAnnotation]): Map[String, SidebandAnnotation] = seq.groupBy(_.ofModule).mapValues(_.reduce(_ ++ _)) +} + +object SidebandAnnotationSeq { + def fromFiles(files: Seq[String]): Seq[SidebandAnnotation] = files.flatMap(x => fromFile(x)) + def fromFile(file: String): Seq[SidebandAnnotation] = { + val source = scala.io.Source.fromFile(file) + val annos = SidebandAnnotationSerialization.deserialize(source.getLines.mkString("\n")) + source.close() + annos + } +} + diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/SidebandAnnotationPass.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/SidebandAnnotationPass.scala new file mode 100644 index 00000000..8cf5f7f8 --- /dev/null +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/SidebandAnnotationPass.scala @@ -0,0 +1,81 @@ +// See LICENSE for license details +package barstools.floorplan.compiler + +import barstools.floorplan._ +import scala.collection.Map + +class SidebandAnnotationPass(val sbMap: Map[String, SidebandAnnotation]) extends Pass { + def execute(state: FloorplanState): FloorplanState = { + val newRecords = state.records.map { record => + val newElement = record.element match { + case e: ConstrainedLogicRect => + sbMap.get(e.ofModule).map(sb => + e.copy( + width = e.width.and(sb.widthConstraint), + height = e.height.and(sb.heightConstraint), + area = e.area.and(sb.areaConstraint) + ) + ).getOrElse(e) + case e: SizedLogicRect => + sbMap.get(e.ofModule).map(sb => + e.copy( + width = sb.width.getOrElse(e.width), + height = sb.height.getOrElse(e.height) + ) + ).getOrElse(e) + case e: PlacedLogicRect => + sbMap.get(e.ofModule).map(sb => + e.copy( + width = sb.width.getOrElse(e.width), + height = sb.height.getOrElse(e.height) + ) + ).getOrElse(e) + case e: ConstrainedHierarchicalTop => + sbMap.get(e.ofModule).map(sb => + e.copy( + width = e.width.and(sb.widthConstraint), + height = e.height.and(sb.heightConstraint), + area = e.area.and(sb.areaConstraint) + ) + ).getOrElse(e) + case e: PlacedHierarchicalTop => + sbMap.get(e.ofModule).map({sb => + e.copy( + width = sb.width.getOrElse(e.width), + height = sb.height.getOrElse(e.height) + ) + }).getOrElse(e) + case e: AbstractMacro => + sbMap.get(e.ofModule).map({sb => + assert(sb.width.isDefined && sb.height.isDefined, "Macro sideband annotations must include width and height") + SizedMacro( + name = e.name, + ofModule = e.ofModule, + width = sb.width.get, + height = sb.height.get + ) + }).getOrElse(e) + case e: SizedMacro => + sbMap.get(e.ofModule).map({sb => + assert(sb.width.isDefined && sb.height.isDefined, "Macro sideband annotations must include width and height") + e.copy( + width = sb.width.get, + height = sb.height.get + ) + }).getOrElse(e) + case e: PlacedMacro => + sbMap.get(e.ofModule).map({sb => + assert(sb.width.isDefined && sb.height.isDefined, "Macro sideband annotations must include width and height") + e.copy( + width = sb.width.get, + height = sb.height.get + ) + }).getOrElse(e) + case e => e + } + record.copy(element = newElement) + } + state.copy(records = newRecords) + } +} + diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/SidebandAnnotationSerialization.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/SidebandAnnotationSerialization.scala new file mode 100644 index 00000000..428eaa98 --- /dev/null +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/SidebandAnnotationSerialization.scala @@ -0,0 +1,19 @@ +// See LICENSE for license details +package barstools.floorplan.compiler + +import org.json4s._ +import org.json4s.native.Serialization.{read, write, writePretty} + +object SidebandAnnotationSerialization { + + val formats = DefaultFormats.skippingEmptyValues + + def serialize(seq: Seq[SidebandAnnotation]): String = writePretty(seq)(formats) + + def deserialize(str: String): Seq[SidebandAnnotation] = { + implicit val formats = SidebandAnnotationSerialization.formats + read[Seq[SidebandAnnotation]](str) + } + +} + diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/TransformMemsPass.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/TransformMemsPass.scala index 5d77c1e3..f20a23f5 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/TransformMemsPass.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/TransformMemsPass.scala @@ -65,7 +65,7 @@ class TransformMemsPass(instMap: Map[OfModule, Seq[(Instance, OfModule)]]) exten case _ => record } }) - state.copy(records = newRecords) + state.copy(records = newRecords, level = 2) } } From cf1bc0c6fa0c7c2c3f496106e0ebd857529567ad Mon Sep 17 00:00:00 2001 From: John Wright Date: Sun, 18 Jul 2021 02:50:40 -0700 Subject: [PATCH 40/73] Clean up ofModule --- .../barstools/floorplan/FloorplanState.scala | 2 +- .../main/scala/barstools/floorplan/IR.scala | 13 ++--------- .../barstools/floorplan/chisel/API.scala | 11 +++------- .../floorplan/compiler/FloorplanTree.scala | 2 ++ .../compiler/SidebandAnnotationPass.scala | 18 +++++++-------- .../compiler/TransformMemsPass.scala | 12 +++++----- .../barstools/floorplan/firrtl/Passes.scala | 22 +++++++++---------- .../barstools/floorplan/hammer/HammerIR.scala | 2 +- 8 files changed, 35 insertions(+), 47 deletions(-) diff --git a/floorplan/src/main/scala/barstools/floorplan/FloorplanState.scala b/floorplan/src/main/scala/barstools/floorplan/FloorplanState.scala index 7531e3eb..2cfb789d 100644 --- a/floorplan/src/main/scala/barstools/floorplan/FloorplanState.scala +++ b/floorplan/src/main/scala/barstools/floorplan/FloorplanState.scala @@ -4,7 +4,7 @@ package barstools.floorplan import barstools.floorplan.hammer.HammerIR import java.io.{File, FileWriter} -final case class FloorplanElementRecord(root: String, inst: Option[String], element: Element, memExt: Option[String] = None) { +final case class FloorplanElementRecord(root: String, inst: Option[String], ofModule: Option[String], element: Element, memExt: Option[String] = None) { def fullPath = root + inst.map(x => "/" + x).getOrElse("") } diff --git a/floorplan/src/main/scala/barstools/floorplan/IR.scala b/floorplan/src/main/scala/barstools/floorplan/IR.scala index d2048052..bab73440 100644 --- a/floorplan/src/main/scala/barstools/floorplan/IR.scala +++ b/floorplan/src/main/scala/barstools/floorplan/IR.scala @@ -25,8 +25,7 @@ sealed abstract class Group extends Element { ////////////////////////////////////////////// Hierarchical barrier private[floorplan] final case class HierarchicalBarrier( - name: String, - ofModule: String + name: String ) extends Primitive { final def level = 0 } @@ -100,7 +99,6 @@ object Margins { private[floorplan] final case class ConstrainedLogicRect( name: String, - ofModule: String, width: Constraint, height: Constraint, area: Constraint, @@ -110,7 +108,6 @@ private[floorplan] final case class ConstrainedLogicRect( private[floorplan] final case class SizedLogicRect( name: String, - ofModule: String, width: BigDecimal, height: BigDecimal, hardBoundary: Boolean @@ -118,7 +115,6 @@ private[floorplan] final case class SizedLogicRect( private[floorplan] final case class PlacedLogicRect( name: String, - ofModule: String, x: BigDecimal, y: BigDecimal, width: BigDecimal, @@ -129,7 +125,6 @@ private[floorplan] final case class PlacedLogicRect( private[floorplan] final case class ConstrainedHierarchicalTop( name: String, - ofModule: String, width: Constraint, height: Constraint, area: Constraint, @@ -140,7 +135,6 @@ private[floorplan] final case class ConstrainedHierarchicalTop( private[floorplan] final case class PlacedHierarchicalTop( name: String, - ofModule: String, width: BigDecimal, height: BigDecimal, margins: Margins, @@ -221,14 +215,12 @@ private[floorplan] final case class MemMacroArray( // Reference to a macro blackbox with unknown dimensions // Do not use for SyncReadMem objects; use MemElement instead private[floorplan] final case class AbstractMacro ( - name: String, - ofModule: String + name: String ) extends AbstractRectPrimitive // Reference to a macro blackbox that has known dimensions private[floorplan] final case class SizedMacro ( name: String, - ofModule: String, // TODO we should replace this with the OfModule in the Record width: BigDecimal, height: BigDecimal ) extends SizedRectPrimitive @@ -236,7 +228,6 @@ private[floorplan] final case class SizedMacro ( // Reference to a macro blackbox that has known dimensions and has been placed private[floorplan] final case class PlacedMacro ( name: String, - ofModule: String, x: BigDecimal, y: BigDecimal, width: BigDecimal, diff --git a/floorplan/src/main/scala/barstools/floorplan/chisel/API.scala b/floorplan/src/main/scala/barstools/floorplan/chisel/API.scala index ec9c8f17..d4796236 100644 --- a/floorplan/src/main/scala/barstools/floorplan/chisel/API.scala +++ b/floorplan/src/main/scala/barstools/floorplan/chisel/API.scala @@ -289,12 +289,7 @@ final class ChiselHierarchicalTop private[chisel] ( val margins: Margins, val hardBoundary: Boolean ) extends ChiselNoReferenceElement(root, name) { - val ofModule = root match { - case t: InstanceTarget => t.ofModule - case t: ModuleTarget => t.module - case _ => ??? - } - protected def generateElement(): Element = ConstrainedHierarchicalTop(name, ofModule, width, height, area, aspectRatio, margins, hardBoundary) + protected def generateElement(): Element = ConstrainedHierarchicalTop(name, width, height, area, aspectRatio, margins, hardBoundary) } final class ChiselHierarchicalBarrier private[chisel] ( @@ -302,7 +297,7 @@ final class ChiselHierarchicalBarrier private[chisel] ( name: String, instance: InstanceTarget ) extends ChiselInstanceElement(root, name, instance) { - protected def generateElement(): Element = HierarchicalBarrier(name, instance.ofModule) + protected def generateElement(): Element = HierarchicalBarrier(name) } final class ChiselLogicRect private[chisel] ( @@ -315,7 +310,7 @@ final class ChiselLogicRect private[chisel] ( val aspectRatio: Constraint, val hardBoundary: Boolean ) extends ChiselInstanceElement(root, name, instance) { - protected def generateElement(): Element = ConstrainedLogicRect(name, instance.ofModule, width, height, area, aspectRatio, hardBoundary) + protected def generateElement(): Element = ConstrainedLogicRect(name, width, height, area, aspectRatio, hardBoundary) } final class ChiselSpacerRect private[chisel] ( diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanTree.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanTree.scala index 2b2e54a1..2e35086c 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanTree.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanTree.scala @@ -3,6 +3,7 @@ package barstools.floorplan.compiler import scala.collection.mutable.{HashMap} import barstools.floorplan.{Element, FloorplanState, FloorplanElementRecord} +/* // TODO all of this is out of date @@ -100,3 +101,4 @@ object FloorplanTree { def apply(state: FloorplanState) = new FloorplanTree(state) } +*/ diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/SidebandAnnotationPass.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/SidebandAnnotationPass.scala index 8cf5f7f8..34b60f94 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/SidebandAnnotationPass.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/SidebandAnnotationPass.scala @@ -7,9 +7,10 @@ import scala.collection.Map class SidebandAnnotationPass(val sbMap: Map[String, SidebandAnnotation]) extends Pass { def execute(state: FloorplanState): FloorplanState = { val newRecords = state.records.map { record => + val ofModule = record.ofModule.getOrElse("") val newElement = record.element match { case e: ConstrainedLogicRect => - sbMap.get(e.ofModule).map(sb => + sbMap.get(ofModule).map(sb => e.copy( width = e.width.and(sb.widthConstraint), height = e.height.and(sb.heightConstraint), @@ -17,21 +18,21 @@ class SidebandAnnotationPass(val sbMap: Map[String, SidebandAnnotation]) extends ) ).getOrElse(e) case e: SizedLogicRect => - sbMap.get(e.ofModule).map(sb => + sbMap.get(ofModule).map(sb => e.copy( width = sb.width.getOrElse(e.width), height = sb.height.getOrElse(e.height) ) ).getOrElse(e) case e: PlacedLogicRect => - sbMap.get(e.ofModule).map(sb => + sbMap.get(ofModule).map(sb => e.copy( width = sb.width.getOrElse(e.width), height = sb.height.getOrElse(e.height) ) ).getOrElse(e) case e: ConstrainedHierarchicalTop => - sbMap.get(e.ofModule).map(sb => + sbMap.get(ofModule).map(sb => e.copy( width = e.width.and(sb.widthConstraint), height = e.height.and(sb.heightConstraint), @@ -39,24 +40,23 @@ class SidebandAnnotationPass(val sbMap: Map[String, SidebandAnnotation]) extends ) ).getOrElse(e) case e: PlacedHierarchicalTop => - sbMap.get(e.ofModule).map({sb => + sbMap.get(ofModule).map({sb => e.copy( width = sb.width.getOrElse(e.width), height = sb.height.getOrElse(e.height) ) }).getOrElse(e) case e: AbstractMacro => - sbMap.get(e.ofModule).map({sb => + sbMap.get(ofModule).map({sb => assert(sb.width.isDefined && sb.height.isDefined, "Macro sideband annotations must include width and height") SizedMacro( name = e.name, - ofModule = e.ofModule, width = sb.width.get, height = sb.height.get ) }).getOrElse(e) case e: SizedMacro => - sbMap.get(e.ofModule).map({sb => + sbMap.get(ofModule).map({sb => assert(sb.width.isDefined && sb.height.isDefined, "Macro sideband annotations must include width and height") e.copy( width = sb.width.get, @@ -64,7 +64,7 @@ class SidebandAnnotationPass(val sbMap: Map[String, SidebandAnnotation]) extends ) }).getOrElse(e) case e: PlacedMacro => - sbMap.get(e.ofModule).map({sb => + sbMap.get(ofModule).map({sb => assert(sb.width.isDefined && sb.height.isDefined, "Macro sideband annotations must include width and height") e.copy( width = sb.width.get, diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/TransformMemsPass.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/TransformMemsPass.scala index f20a23f5..fe552f6b 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/TransformMemsPass.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/TransformMemsPass.scala @@ -32,15 +32,15 @@ class TransformMemsPass(instMap: Map[OfModule, Seq[(Instance, OfModule)]]) exten record.element match { case e: MemElement => // TODO fail gracefully if key does not exist - instMap(OfModule(record.memExt.get)).map { case (inst: Instance, ofMod: OfModule) => + instMap(OfModule(record.ofModule.get)).map { case (inst: Instance, ofMod: OfModule) => nameSet.remove(e.name) - val element = AbstractMacro(getUniqueName(e.name + "_" + ofMod.value), ofModule = ofMod.value) + val element = AbstractMacro(getUniqueName(e.name + "_" + ofMod.value)) renameMap.update(e.name, renameMap.getOrElse(e.name, Set()) ++ Set(element.name)) FloorplanElementRecord( root = record.root, inst = record.inst.map(_ + "/" + inst.value), - element = element, - memExt = None + ofModule = Some(ofMod.value), + element = element ) } case _ => Seq(record) @@ -59,8 +59,8 @@ class TransformMemsPass(instMap: Map[OfModule, Seq[(Instance, OfModule)]]) exten FloorplanElementRecord( root = record.root, inst = record.inst, // should always be None - element = element, - memExt = None + ofModule = None, + element = element ) case _ => record } diff --git a/floorplan/src/main/scala/barstools/floorplan/firrtl/Passes.scala b/floorplan/src/main/scala/barstools/floorplan/firrtl/Passes.scala index 7d2e1ec6..47173ee1 100644 --- a/floorplan/src/main/scala/barstools/floorplan/firrtl/Passes.scala +++ b/floorplan/src/main/scala/barstools/floorplan/firrtl/Passes.scala @@ -43,19 +43,19 @@ class GenerateFloorplanIRPass extends Transform with RegisteredTransform with De }).getOrElse("") } - def newRecord(path: String, ref: Option[String], anno: FloorplanAnnotation, ext: Option[String] = None) = - FloorplanElementRecord(path, ref, FloorplanSerialization.deserialize(anno.fpir), ext) + def newRecord(path: String, ref: Option[String], ofModule: Option[String], anno: FloorplanAnnotation) = + FloorplanElementRecord(path, ref, ofModule, FloorplanSerialization.deserialize(anno.fpir)) val list = state.annotations.collect({ case x: NoReferenceFloorplanAnnotation => - val rootTarget = x.target match { - case y: InstanceTarget => Some(y) + val (rootTarget, ofModule) = x.target match { + case y: InstanceTarget => (Some(y), Some(y.ofModule)) case y: ModuleTarget => assert(y.module == state.circuit.main, "ModuleTarget is only supported for the top module") - Option.empty[InstanceTarget] + (Option.empty[InstanceTarget], Some(y.module)) case _ => ??? } - newRecord(getInstancePath(rootTarget), None, x) + newRecord(getInstancePath(rootTarget), None, ofModule, x) case x: InstanceFloorplanAnnotation if x.targets.flatten.length == 2 => val rootTarget = x.targets(0)(0) match { case y: InstanceTarget => Some(y) @@ -64,14 +64,14 @@ class GenerateFloorplanIRPass extends Transform with RegisteredTransform with De Option.empty[InstanceTarget] case _ => ??? } - val instTarget = x.targets(1)(0) match { - case y: InstanceTarget => Some(y) + val (instTarget, ofModule) = x.targets(1)(0) match { + case y: InstanceTarget => (Some(y), Some(y.ofModule)) case y: ModuleTarget => assert(y.module == state.circuit.main, "ModuleTarget is only supported for the top module") - Option.empty[InstanceTarget] + (Option.empty[InstanceTarget], Some(y.module)) case _ => ??? } - newRecord(getInstancePath(rootTarget), Some(getRelativePath(rootTarget, instTarget)), x) + newRecord(getInstancePath(rootTarget), Some(getRelativePath(rootTarget, instTarget)), ofModule, x) case x: MemFloorplanAnnotation if x.targets.flatten.length == 2 => val rootTarget = x.targets(0)(0) match { case y: InstanceTarget => Some(y) @@ -95,7 +95,7 @@ class GenerateFloorplanIRPass extends Transform with RegisteredTransform with De instance=ext, ofModule=ext, path=refTarget.path :+ (Instance(refTarget.ref), OfModule(mem))) - newRecord(getInstancePath(rootTarget), Some(getRelativePath(rootTarget, Some(newTarget))), x, Some(ext)) + newRecord(getInstancePath(rootTarget), Some(getRelativePath(rootTarget, Some(newTarget))), Some(ext), x) }) val filename = state.annotations.collectFirst({ diff --git a/floorplan/src/main/scala/barstools/floorplan/hammer/HammerIR.scala b/floorplan/src/main/scala/barstools/floorplan/hammer/HammerIR.scala index a28b3ef1..a77c767e 100644 --- a/floorplan/src/main/scala/barstools/floorplan/hammer/HammerIR.scala +++ b/floorplan/src/main/scala/barstools/floorplan/hammer/HammerIR.scala @@ -24,7 +24,7 @@ object HammerIR { create_physical = Some(false), width = toMicrons(c.width), height = toMicrons(c.height), - master = Some(c.ofModule), + master = record.ofModule, margins = None, top_layer = None, // TODO need this for macros layers = None, // TODO need this for macros From cf77be6c499f52ec5952abf8cc96d5381f3b8c1b Mon Sep 17 00:00:00 2001 From: John Wright Date: Sun, 18 Jul 2021 10:40:59 -0700 Subject: [PATCH 41/73] try to un-break sbt test --- build.sbt | 12 ++++-------- .../scala/barstools/macros/MacroCompilerSpec.scala | 1 + 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/build.sbt b/build.sbt index 63ea3ac2..863b2ca6 100644 --- a/build.sbt +++ b/build.sbt @@ -39,16 +39,12 @@ lazy val macros = (project in file("macros")) )) .enablePlugins(sbtassembly.AssemblyPlugin) -lazy val tapeout = (project in file("tapeout")) +lazy val floorplan = (project in file("floorplan")) .settings(commonSettings) - .settings(Seq( - libraryDependencies ++= Seq( - "io.github.daviddenton" %% "handlebars-scala-fork" % "2.3.0" - ) - )) - .settings(scalacOptions in Test ++= Seq("-language:reflectiveCalls")) -lazy val floorplan = (project in file("floorplan")) +lazy val tapeout = (project in file("tapeout")) + .dependsOn(floorplan) .settings(commonSettings) + .settings(scalacOptions in Test ++= Seq("-language:reflectiveCalls")) lazy val root = (project in file(".")).aggregate(macros, tapeout, floorplan) diff --git a/src/test/scala/barstools/macros/MacroCompilerSpec.scala b/src/test/scala/barstools/macros/MacroCompilerSpec.scala index 2cfcaed5..235bcbd1 100644 --- a/src/test/scala/barstools/macros/MacroCompilerSpec.scala +++ b/src/test/scala/barstools/macros/MacroCompilerSpec.scala @@ -119,6 +119,7 @@ abstract class MacroCompilerSpec extends AnyFlatSpec with Matchers { libs, None, None, + None, getCostMetric, if (synflops) MacroCompilerAnnotation.Synflops else MacroCompilerAnnotation.Default ), From 79b458b42e545fd9d4c5025e0218b76d9c086624 Mon Sep 17 00:00:00 2001 From: John Wright Date: Sun, 18 Jul 2021 12:22:20 -0700 Subject: [PATCH 42/73] Add parents to IR --- .../barstools/floorplan/FloorplanState.scala | 2 +- .../main/scala/barstools/floorplan/IR.scala | 43 +++++++++--- .../barstools/floorplan/chisel/API.scala | 65 +++++++++++++++---- .../compiler/SidebandAnnotationPass.scala | 1 + .../compiler/TransformMemsPass.scala | 3 +- 5 files changed, 89 insertions(+), 25 deletions(-) diff --git a/floorplan/src/main/scala/barstools/floorplan/FloorplanState.scala b/floorplan/src/main/scala/barstools/floorplan/FloorplanState.scala index 2cfb789d..bd80cdcb 100644 --- a/floorplan/src/main/scala/barstools/floorplan/FloorplanState.scala +++ b/floorplan/src/main/scala/barstools/floorplan/FloorplanState.scala @@ -4,7 +4,7 @@ package barstools.floorplan import barstools.floorplan.hammer.HammerIR import java.io.{File, FileWriter} -final case class FloorplanElementRecord(root: String, inst: Option[String], ofModule: Option[String], element: Element, memExt: Option[String] = None) { +final case class FloorplanElementRecord(root: String, inst: Option[String], ofModule: Option[String], element: Element) { def fullPath = root + inst.map(x => "/" + x).getOrElse("") } diff --git a/floorplan/src/main/scala/barstools/floorplan/IR.scala b/floorplan/src/main/scala/barstools/floorplan/IR.scala index bab73440..1d82d27b 100644 --- a/floorplan/src/main/scala/barstools/floorplan/IR.scala +++ b/floorplan/src/main/scala/barstools/floorplan/IR.scala @@ -16,16 +16,23 @@ sealed abstract class Element { def serialize = FloorplanSerialization.serialize(this) } -sealed abstract class Primitive extends Element +sealed abstract class ElementWithParent extends Element { + def parent: String +} + +sealed abstract class Primitive extends ElementWithParent -sealed abstract class Group extends Element { +sealed abstract class Group extends ElementWithParent { def elements: Seq[Option[String]] } +sealed abstract class Top extends Element + ////////////////////////////////////////////// Hierarchical barrier private[floorplan] final case class HierarchicalBarrier( - name: String + name: String, + parent: String ) extends Primitive { final def level = 0 } @@ -73,6 +80,7 @@ sealed abstract class PlacedRectPrimitive extends Primitive with PlacedRectLike private[floorplan] final case class ConstrainedSpacerRect( name: String, + parent: String, width: Constraint = Unconstrained(), height: Constraint = Unconstrained(), area: Constraint = Unconstrained(), @@ -81,6 +89,7 @@ private[floorplan] final case class ConstrainedSpacerRect( private[floorplan] final case class SizedSpacerRect( name: String, + parent: String, x: BigDecimal, y: BigDecimal, width: BigDecimal, @@ -99,6 +108,7 @@ object Margins { private[floorplan] final case class ConstrainedLogicRect( name: String, + parent: String, width: Constraint, height: Constraint, area: Constraint, @@ -108,6 +118,7 @@ private[floorplan] final case class ConstrainedLogicRect( private[floorplan] final case class SizedLogicRect( name: String, + parent: String, width: BigDecimal, height: BigDecimal, hardBoundary: Boolean @@ -115,6 +126,7 @@ private[floorplan] final case class SizedLogicRect( private[floorplan] final case class PlacedLogicRect( name: String, + parent: String, x: BigDecimal, y: BigDecimal, width: BigDecimal, @@ -125,23 +137,28 @@ private[floorplan] final case class PlacedLogicRect( private[floorplan] final case class ConstrainedHierarchicalTop( name: String, + topGroup: String, width: Constraint, height: Constraint, area: Constraint, aspectRatio: Constraint, margins: Margins, hardBoundary: Boolean -) extends ConstrainedRectPrimitive +) extends Top with ConstrainedRectLike { + final def level = 2 +} private[floorplan] final case class PlacedHierarchicalTop( name: String, + topGroup: String, width: BigDecimal, height: BigDecimal, margins: Margins, hardBoundary: Boolean -) extends PlacedRectPrimitive { - def x = BigDecimal(0) - def y = BigDecimal(0) +) extends Top with PlacedRectLike { + final def x = BigDecimal(0) + final def y = BigDecimal(0) + final def level = 0 } ////////////////////////////////////////////// Aggregate (Group) things @@ -159,6 +176,7 @@ sealed abstract class Grid extends Group { private[floorplan] final case class WeightedGrid( name: String, + parent: String, xDim: Int, yDim: Int, elements: Seq[Option[String]], @@ -170,6 +188,7 @@ private[floorplan] final case class WeightedGrid( private[floorplan] final case class ElasticGrid( name: String, + parent: String, xDim: Int, yDim: Int, elements: Seq[Option[String]] @@ -182,12 +201,14 @@ private[floorplan] final case class ElasticGrid( // Reference to a MemElement private[floorplan] final case class MemElement( - name: String + name: String, + parent: String ) extends AbstractRectPrimitive // Container for MemElements private[floorplan] final case class MemElementArray( name: String, + parent: String, elements: Seq[Option[String]], width: Constraint = Unconstrained(), height: Constraint = Unconstrained(), @@ -203,6 +224,7 @@ private[floorplan] final case class MemElementArray( // are more commonly arrayed private[floorplan] final case class MemMacroArray( name: String, + parent: String, elements: Seq[Option[String]], width: Constraint = Unconstrained(), height: Constraint = Unconstrained(), @@ -215,12 +237,14 @@ private[floorplan] final case class MemMacroArray( // Reference to a macro blackbox with unknown dimensions // Do not use for SyncReadMem objects; use MemElement instead private[floorplan] final case class AbstractMacro ( - name: String + name: String, + parent: String ) extends AbstractRectPrimitive // Reference to a macro blackbox that has known dimensions private[floorplan] final case class SizedMacro ( name: String, + parent: String, width: BigDecimal, height: BigDecimal ) extends SizedRectPrimitive @@ -228,6 +252,7 @@ private[floorplan] final case class SizedMacro ( // Reference to a macro blackbox that has known dimensions and has been placed private[floorplan] final case class PlacedMacro ( name: String, + parent: String, x: BigDecimal, y: BigDecimal, width: BigDecimal, diff --git a/floorplan/src/main/scala/barstools/floorplan/chisel/API.scala b/floorplan/src/main/scala/barstools/floorplan/chisel/API.scala index d4796236..ec4dcc01 100644 --- a/floorplan/src/main/scala/barstools/floorplan/chisel/API.scala +++ b/floorplan/src/main/scala/barstools/floorplan/chisel/API.scala @@ -12,7 +12,7 @@ import scala.math.{BigInt, BigDecimal} final case class ChiselFloorplanException(message: String) extends Exception(message: String) -final class ChiselFloorplanContext private[chisel] (val root: Target, topElement: ChiselElement) { +final class ChiselFloorplanContext private[chisel] (val root: Target, val topElement: ChiselHierarchicalTop) { private[chisel] val elementBuf = new ArrayBuffer[ChiselElement]() @@ -68,7 +68,7 @@ final class ChiselFloorplanContext private[chisel] (val root: Target, topElement def createElasticArray(dim: Int): ChiselElasticArray = createElasticArray(dim, Direction.Horizontal, None) def createElasticArray(elts: Seq[ChiselElement], dir: Direction = Direction.Horizontal, name: Option[String] = None): ChiselElasticArray = { val ary = createElasticArray(elts.length, dir, name) - elts.zipWithIndex.foreach { case (e, i) => ary.placeElementAt(e, i) } + elts.zipWithIndex.foreach { case (e, i) => ary.placeAt(e, i) } ary } @@ -92,6 +92,11 @@ final class ChiselFloorplanContext private[chisel] (val root: Target, topElement addElement(elt) } + def setTopGroup[T <: ChiselGroupElement](g: T): T = { + topElement.setTopGroup(g) + g + } + } object Floorplan { @@ -223,6 +228,20 @@ sealed abstract class ChiselElement(val root: Target, val name: String) { protected def generateElement(): Element private[chisel] def getFloorplanAnnotations(): Seq[FloorplanAnnotation] def getAnnotations(): Seq[Annotation] = getFloorplanAnnotations() + private var parent = Option.empty[CanBeParent] + private[chisel] def setParent(p: CanBeParent) { + assert(!this.parent.isDefined, "Element cannot have multiple parents") + this.parent = Some(p) + } + protected def parentName: String = { + assert(this.parent.isDefined, "Parent is not defined for this element") + this.parent.get.name + } +} + +sealed trait CanBeParent { + this: ChiselElement => + def name: String } sealed abstract class ChiselNoReferenceElement(root: Target, name: String) extends ChiselElement(root, name) { @@ -237,7 +256,9 @@ sealed abstract class ChiselMemElement(root: Target, name: String, val reference private[chisel] def getFloorplanAnnotations() = Seq(MemFloorplanAnnotation(Seq(Seq(root), Seq(reference)), generateElement())) } -sealed abstract class ChiselGroupElement(root: Target, name: String, val context: ChiselFloorplanContext) extends ChiselElement(root, name) { +sealed abstract class ChiselGroupElement(root: Target, name: String, val context: ChiselFloorplanContext) + extends ChiselElement(root, name) with CanBeParent { + protected def initialSize: Int protected val elements = Seq.fill(initialSize)(Option.empty[ChiselElement]).toBuffer protected var isCommitted = false @@ -249,13 +270,15 @@ sealed abstract class ChiselGroupElement(root: Target, name: String, val context generateGroupElement(elements.map(_.map(_.name))) } - private[chisel] def _placeElementAt(e: ChiselElement, idx: Int) { + private[chisel] def _placeAt[T <: ChiselElement](e: T, idx: Int): T = { assert(!isCommitted, "Cannot add elements after committing") // This is only supported in scala 2.13 //elements.padToInPlace(idx+1, None) for (i <- elements.length until (idx+1)) elements += None assert(!elements(idx).isDefined, "Already added at this location") elements(idx) = Some(e) + e.setParent(this) + e } private[chisel] def getFloorplanAnnotations() = Seq(NoReferenceFloorplanAnnotation(root, generateElement())) @@ -266,6 +289,7 @@ abstract class ChiselArrayElement(root: Target, name: String, context: ChiselFlo private[chisel] def addElement(e: ChiselElement) { assert(!isCommitted, "Cannot add elements after committing") elements += Some(e) + e.setParent(this) } } @@ -275,7 +299,7 @@ abstract class ChiselGridElement(root: Target, name: String, context: ChiselFloo protected def initialSize = xDim * yDim protected def toIdx(x: Int, y: Int): Int = xDim*y + x - def placeElementAt(e: ChiselElement, x: Int, y: Int) { _placeElementAt(e, toIdx(x, y)) } + def placeAt[T <: ChiselElement](e: T, x: Int, y: Int): T = _placeAt(e, toIdx(x, y)) } @@ -288,8 +312,21 @@ final class ChiselHierarchicalTop private[chisel] ( val aspectRatio: Constraint, val margins: Margins, val hardBoundary: Boolean -) extends ChiselNoReferenceElement(root, name) { - protected def generateElement(): Element = ConstrainedHierarchicalTop(name, width, height, area, aspectRatio, margins, hardBoundary) +) extends ChiselNoReferenceElement(root, name) with CanBeParent { + private var topGroup = Option.empty[ChiselGroupElement] + + private def topGroupName: String = { + assert(this.topGroup.isDefined, "HierarchicalTop needs a topGroup") + this.topGroup.get.name + } + + protected def generateElement(): Element = ConstrainedHierarchicalTop(name, topGroupName, width, height, area, aspectRatio, margins, hardBoundary) + + private[chisel] def setTopGroup(t: ChiselGroupElement) { + assert(!this.topGroup.isDefined, "Cannot set topGroup twice") + t.setParent(this) + this.topGroup = Some(t) + } } final class ChiselHierarchicalBarrier private[chisel] ( @@ -297,7 +334,7 @@ final class ChiselHierarchicalBarrier private[chisel] ( name: String, instance: InstanceTarget ) extends ChiselInstanceElement(root, name, instance) { - protected def generateElement(): Element = HierarchicalBarrier(name) + protected def generateElement(): Element = HierarchicalBarrier(name, parentName) } final class ChiselLogicRect private[chisel] ( @@ -310,7 +347,7 @@ final class ChiselLogicRect private[chisel] ( val aspectRatio: Constraint, val hardBoundary: Boolean ) extends ChiselInstanceElement(root, name, instance) { - protected def generateElement(): Element = ConstrainedLogicRect(name, width, height, area, aspectRatio, hardBoundary) + protected def generateElement(): Element = ConstrainedLogicRect(name, parentName, width, height, area, aspectRatio, hardBoundary) } final class ChiselSpacerRect private[chisel] ( @@ -321,7 +358,7 @@ final class ChiselSpacerRect private[chisel] ( val area: Constraint = Unconstrained(), val aspectRatio: Constraint = Unconstrained() ) extends ChiselNoReferenceElement(root, name) { - protected def generateElement(): Element = ConstrainedSpacerRect(name, width, height, area, aspectRatio) + protected def generateElement(): Element = ConstrainedSpacerRect(name, parentName, width, height, area, aspectRatio) } final class ChiselMem private[chisel] ( @@ -329,7 +366,7 @@ final class ChiselMem private[chisel] ( name: String, reference: ReferenceTarget ) extends ChiselMemElement(root, name, reference) { - protected def generateElement(): Element = MemElement(name) + protected def generateElement(): Element = MemElement(name, parentName) } final class ChiselMemArray private[chisel] ( @@ -338,7 +375,7 @@ final class ChiselMemArray private[chisel] ( context: ChiselFloorplanContext ) extends ChiselArrayElement(root, name, context) { protected def initialSize = 0 - protected def generateGroupElement(names: Seq[Option[String]]): Group = MemElementArray(name, names) + protected def generateGroupElement(names: Seq[Option[String]]): Group = MemElementArray(name, parentName, names) def addMem[T <: Data](mem: MemBase[T]) = this.addElement(this.context.addMem(mem)) } @@ -350,7 +387,7 @@ class ChiselElasticGrid private[chisel] ( xDim: Int, yDim: Int ) extends ChiselGridElement(root, name, context, xDim, yDim) { - final protected def generateGroupElement(names: Seq[Option[String]]): Group = ElasticGrid(name, xDim, yDim, names) + final protected def generateGroupElement(names: Seq[Option[String]]): Group = ElasticGrid(name, parentName, xDim, yDim, names) } class ChiselElasticArray private[chisel] ( @@ -360,5 +397,5 @@ class ChiselElasticArray private[chisel] ( dim: Int, val dir: Direction ) extends ChiselElasticGrid(root, name, context, dir.ifH(dim,1), dir.ifV(dim,1)) { - def placeElementAt(e: ChiselElement, i: Int) { placeElementAt(e, dir.ifH(i,0), dir.ifV(0,i)) } + def placeAt[T <: ChiselElement](e: T, i: Int): T = placeAt(e, dir.ifH(i,0), dir.ifV(0,i)) } diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/SidebandAnnotationPass.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/SidebandAnnotationPass.scala index 34b60f94..0dce3131 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/SidebandAnnotationPass.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/SidebandAnnotationPass.scala @@ -51,6 +51,7 @@ class SidebandAnnotationPass(val sbMap: Map[String, SidebandAnnotation]) extends assert(sb.width.isDefined && sb.height.isDefined, "Macro sideband annotations must include width and height") SizedMacro( name = e.name, + parent = e.parent, width = sb.width.get, height = sb.height.get ) diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/TransformMemsPass.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/TransformMemsPass.scala index fe552f6b..66bfa919 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/TransformMemsPass.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/TransformMemsPass.scala @@ -34,7 +34,7 @@ class TransformMemsPass(instMap: Map[OfModule, Seq[(Instance, OfModule)]]) exten // TODO fail gracefully if key does not exist instMap(OfModule(record.ofModule.get)).map { case (inst: Instance, ofMod: OfModule) => nameSet.remove(e.name) - val element = AbstractMacro(getUniqueName(e.name + "_" + ofMod.value)) + val element = AbstractMacro(getUniqueName(e.name + "_" + ofMod.value), e.parent) renameMap.update(e.name, renameMap.getOrElse(e.name, Set()) ++ Set(element.name)) FloorplanElementRecord( root = record.root, @@ -50,6 +50,7 @@ class TransformMemsPass(instMap: Map[OfModule, Seq[(Instance, OfModule)]]) exten case e: MemElementArray => val element = MemMacroArray( name = e.name, + parent = e.parent, elements = e.elements.filter(_.isDefined).flatMap(x => renameMap(x.get)).map(x => Some(x)), width = e.width, height = e.height, From 6a4ca62d80ef74f269040e76dfec752118bb2f80 Mon Sep 17 00:00:00 2001 From: John Wright Date: Sun, 18 Jul 2021 14:26:39 -0700 Subject: [PATCH 43/73] Implement ReplaceHierarchicalPass --- .../main/scala/barstools/floorplan/IR.scala | 56 +++++++++-- .../compiler/FloorplanCompiler.scala | 7 ++ .../floorplan/compiler/FloorplanTree.scala | 93 ++----------------- .../barstools/floorplan/compiler/Pass.scala | 17 ++-- .../compiler/PropagationPasses.scala | 12 +++ .../compiler/ReplaceHierarchicalPass.scala | 61 ++++++++++++ .../compiler/TransformMemsPass.scala | 2 +- .../barstools/floorplan/firrtl/Passes.scala | 6 +- 8 files changed, 151 insertions(+), 103 deletions(-) create mode 100644 floorplan/src/main/scala/barstools/floorplan/compiler/PropagationPasses.scala create mode 100644 floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceHierarchicalPass.scala diff --git a/floorplan/src/main/scala/barstools/floorplan/IR.scala b/floorplan/src/main/scala/barstools/floorplan/IR.scala index 1d82d27b..35827051 100644 --- a/floorplan/src/main/scala/barstools/floorplan/IR.scala +++ b/floorplan/src/main/scala/barstools/floorplan/IR.scala @@ -34,7 +34,7 @@ private[floorplan] final case class HierarchicalBarrier( name: String, parent: String ) extends Primitive { - final def level = 0 + final def level = 3 } ////////////////////////////////////////////// Rectangular things @@ -59,11 +59,11 @@ sealed trait PlacedRectLike { } object IRLevel { - def max = 3 + def max = 4 } sealed abstract class AbstractRectPrimitive extends Primitive { - final def level = 3 + final def level = 2 } sealed abstract class ConstrainedRectPrimitive extends Primitive with ConstrainedRectLike { @@ -145,7 +145,7 @@ private[floorplan] final case class ConstrainedHierarchicalTop( margins: Margins, hardBoundary: Boolean ) extends Top with ConstrainedRectLike { - final def level = 2 + final def level = 3 } private[floorplan] final case class PlacedHierarchicalTop( @@ -174,6 +174,7 @@ sealed abstract class Grid extends Group { def get(x: Int, y: Int) = elements(xDim*y + x) } +// TODO eventually rename this to AbstractWeightedGrid private[floorplan] final case class WeightedGrid( name: String, parent: String, @@ -183,15 +184,58 @@ private[floorplan] final case class WeightedGrid( weights: Seq[BigDecimal], packed: Boolean ) extends Grid { - def level = 1 + def level = 2 +} + +private[floorplan] final case class ConstrainedWeightedGrid( + name: String, + parent: String, + xDim: Int, + yDim: Int, + elements: Seq[Option[String]], + weights: Seq[BigDecimal], + packed: Boolean, + width: Constraint = Unconstrained(), + height: Constraint = Unconstrained(), + area: Constraint = Unconstrained(), + aspectRatio: Constraint = Unconstrained() +) extends Grid { + def level = 2 } +// TODO eventually rename this to AbstractElasticGrid private[floorplan] final case class ElasticGrid( name: String, parent: String, xDim: Int, yDim: Int, elements: Seq[Option[String]] +) extends Grid { + def level = 2 +} + +private[floorplan] final case class ConstrainedElasticGrid( + name: String, + parent: String, + xDim: Int, + yDim: Int, + elements: Seq[Option[String]], + width: Constraint = Unconstrained(), + height: Constraint = Unconstrained(), + area: Constraint = Unconstrained(), + aspectRatio: Constraint = Unconstrained() +) extends Grid { + def level = 2 +} + +private[floorplan] final case class SizedGrid( + name: String, + parent: String, + xDim: Int, + yDim: Int, + elements: Seq[Option[String]], + widths: Seq[BigDecimal], + heights: Seq[BigDecimal] ) extends Grid { def level = 1 } @@ -215,7 +259,7 @@ private[floorplan] final case class MemElementArray( area: Constraint = Unconstrained(), aspectRatio: Constraint = Unconstrained() ) extends Group with ConstrainedRectLike { - def level = 3 + def level = 4 } // Container for MemElements that have been converted to Macros diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanCompiler.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanCompiler.scala index 93cd17a7..58dc692d 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanCompiler.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanCompiler.scala @@ -5,6 +5,7 @@ import barstools.floorplan._ case class FloorplanOptions( outFile: String = "", + topMod: String = "", outFmt: OutputFormat = OutputFormat.HammerIR, inFiles: Seq[String] = Seq(), sbAnnoFiles: Seq[String] = Seq(), @@ -21,6 +22,12 @@ object FloorplanCompiler extends App { action((x, c) => c.copy(inFiles = c.inFiles :+ x)). text("input file name") + opt[String]('t', "top-module"). + required(). + valueName(""). + action((x, c) => c.copy(topMod = x)). + text("top module name") + opt[String]('o', "output-file"). required(). valueName(""). diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanTree.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanTree.scala index 2e35086c..d6aaa231 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanTree.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanTree.scala @@ -3,102 +3,29 @@ package barstools.floorplan.compiler import scala.collection.mutable.{HashMap} import barstools.floorplan.{Element, FloorplanState, FloorplanElementRecord} + /* -// TODO all of this is out of date +class FloorplanTree(state: FloorplanState, val topMod: String) { -class FloorplanTree(state: FloorplanState) { + assert(state.level < 3, "Cannot have Hierarchicals") - class Node(val parent: Option[Node], val name: String) { + class Node(val parent: Option[Node], val record: FloorplanElementRecord) { val children = new HashMap[String, Node]() - val elements = new HashMap[String, Element]() - - def addElement(path: Seq[String], tag: String, element: Element): Unit = { - if (path.isEmpty) { - if (elements.contains(tag)) { - throw new Exception(s"Duplicate tag ${tag} at floorplan path ${getPath}") - } else { - elements.update(tag, element) - } - } else { - val child = children.getOrElseUpdate(path.head, new Node(Some(this), path.head)) - child.addElement(path.tail, tag, element) - } - } - - def getPathHelper(sub: String): String = parent.map(_.getPathHelper(s"${name}.${sub}")).getOrElse(sub) - - def getPath: String = getPathHelper(name) - - def getNode(path: Seq[String]): Node = { - if (path.isEmpty) { - this - } else { - children.get(path.head).map(_.getNode(path.tail)).getOrElse { - throw new Exception(s"Path ${getPath}.${path} does not exist in tree") - } - } - } + val name = record.element.name - def getElement(tag: String): Element = { - elements.get(tag).getOrElse { throw new Exception(s"Tag ${tag} does not exist in node ${getPath}") } + protected[FloorplanTree] def addNode(node: Node) { + children =+ (node.name -> node) } - def getElement(path: Seq[String], tag: String): Element = (if (path.isEmpty) this else getNode(path)).getElement(tag) - - def getElements: Seq[(String, Element)] = { - elements.toSeq ++ (children.mapValues(_.getElements).toSeq.map { case (name, elts) => - elts.map { case (path, elt) => - val newpath = if (name == "") path else s"${name}.${path}" - (newpath, elt) - } - }).flatten - } - - def mapElements(f: (Element) => Element): Seq[(String, Element)] = { - ??? - } - } - - val root = new Node(None, "") - - private def parsePathAndTag(str: String): (Seq[String], String) = { - val split = str.split("#", -1) - assert(split.size == 2, s"Malformed floorplan record path: ${str}") - val path = split(0).split(".") - val tag = split(1) - (path, tag) - } - - state.records.foreach { record => - val (path, tag) = parsePathAndTag(record.root) - root.addElement(path, tag, record.element) + protected[FloorplanTree] def addRecord(record: FloorplanElementRecord) { addNode(new Node(Some(this), record)) } } - def getNode(path: Seq[String]): Node = root.getNode(path) - - def getNode(str: String): Node = { - if (str.contains("#")) throw new Exception(s"getNode cannot be called on an element path (containing #): ${str}") - getNode(str.split(".")) - } - - def getElement(str: String): Element = { - val (path, tag) = parsePathAndTag(str) - root.getElement(path, tag) - } - - def toState: FloorplanState = { - val records = root.getElements.map { case (path, elt) => FloorplanElementRecord(path, None, elt) } // TODO this is broken but unused right now - FloorplanState(records, records.map(_.element.level).max) - } - - //def traverse( } object FloorplanTree { - - def apply(state: FloorplanState) = new FloorplanTree(state) - + def apply(state: FloorplanState, topMod: String) = new FloorplanTree(state, topMod) } + */ diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/Pass.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/Pass.scala index 12276b48..d0077c98 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/Pass.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/Pass.scala @@ -11,9 +11,11 @@ object Pass { Seq( new TransformMemsPass(instMap), new SidebandAnnotationPass(sbAnnos), - new TopDownPropagationPass, - new BottomUpPropagationPass, - new ResolveConstraintsPass + new ReplaceHierarchicalPass(opts.topMod), + new TopDownPropagationPass(opts.topMod), + new BottomUpPropagationPass(opts.topMod), + new ResolveConstraintsPass, + new CalculatePlacementsPass ) } } @@ -22,15 +24,10 @@ abstract class Pass { def execute(state: FloorplanState): FloorplanState } -class TopDownPropagationPass extends Pass { - def execute(state: FloorplanState): FloorplanState = state // TODO -} - -class BottomUpPropagationPass extends Pass { +class ResolveConstraintsPass extends Pass { def execute(state: FloorplanState): FloorplanState = state // TODO } -class ResolveConstraintsPass extends Pass { +class CalculatePlacementsPass extends Pass { def execute(state: FloorplanState): FloorplanState = state // TODO } - diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/PropagationPasses.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/PropagationPasses.scala new file mode 100644 index 00000000..533e2324 --- /dev/null +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/PropagationPasses.scala @@ -0,0 +1,12 @@ +// See LICENSE for license details +package barstools.floorplan.compiler + +import barstools.floorplan._ + +class TopDownPropagationPass(val topMod: String) extends Pass { + def execute(state: FloorplanState): FloorplanState = state // TODO +} + +class BottomUpPropagationPass(val topMod: String) extends Pass { + def execute(state: FloorplanState): FloorplanState = state // TODO +} diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceHierarchicalPass.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceHierarchicalPass.scala new file mode 100644 index 00000000..719af657 --- /dev/null +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceHierarchicalPass.scala @@ -0,0 +1,61 @@ +// See LICENSE for license details +package barstools.floorplan.compiler + +import scala.collection.mutable.{HashMap} +import barstools.floorplan._ + +class ReplaceHierarchicalPass(val topMod: String) extends Pass { + def execute(state: FloorplanState): FloorplanState = { + + // TODO this pass is for resolving a design to a flattened floorplan. To + // support hierarchical PNR this will need to change + + // Find HierarchicalTop records, then stitch them together + val topMaps = (state.records.flatMap(r => r.element match { + case e: ConstrainedHierarchicalTop => Some((r.fullPath -> r)) + case e: PlacedHierarchicalTop => Some((r.fullPath -> r)) + case _ => None + })).toMap + + // TODO we need to also change the root path for all elements underneath these + val newRecords = state.records.flatMap({r => r.element match { + case e: HierarchicalBarrier => + assert(topMaps.contains(r.fullPath), s"All HierarchicalBarriers must have a corresponding HierarchicalTop: ${r.fullPath} is missing") + val newE = (topMaps(r.fullPath).element match { + case t: ConstrainedHierarchicalTop => + ConstrainedElasticGrid( + name = t.name, + parent = e.parent, + xDim = 1, + yDim = 1, + elements = Seq(Some(t.topGroup)), + width = t.width, + height = t.height, + area = t.area, + aspectRatio = t.aspectRatio + ) + case t: PlacedHierarchicalTop => + SizedGrid( + name = t.name, + parent = e.parent, + xDim = 1, + yDim = 1, + elements = Seq(Some(t.topGroup)), + widths = Seq(t.width), + heights = Seq(t.height) + ) + case _ => ??? + }) + Seq(FloorplanElementRecord( + root = r.root, // TODO clean up path + inst = r.inst, + ofModule = r.ofModule, + element = newE + )) + case e: ConstrainedHierarchicalTop if r.ofModule != Some(topMod) => Seq() + case e: PlacedHierarchicalTop if r.ofModule != Some(topMod) => Seq() + case e => Seq(r) + }}) + state.copy(records = newRecords, level = 2) // TODO recalculate level + } +} diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/TransformMemsPass.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/TransformMemsPass.scala index 66bfa919..cc48ea7c 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/TransformMemsPass.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/TransformMemsPass.scala @@ -66,7 +66,7 @@ class TransformMemsPass(instMap: Map[OfModule, Seq[(Instance, OfModule)]]) exten case _ => record } }) - state.copy(records = newRecords, level = 2) + state.copy(records = newRecords, level = 3) // TODO recalculate level? } } diff --git a/floorplan/src/main/scala/barstools/floorplan/firrtl/Passes.scala b/floorplan/src/main/scala/barstools/floorplan/firrtl/Passes.scala index 47173ee1..8edc310b 100644 --- a/floorplan/src/main/scala/barstools/floorplan/firrtl/Passes.scala +++ b/floorplan/src/main/scala/barstools/floorplan/firrtl/Passes.scala @@ -28,8 +28,8 @@ class GenerateFloorplanIRPass extends Transform with RegisteredTransform with De def execute(state: CircuitState): CircuitState = { def getInstancePath(t: Option[InstanceTarget]): String = t map { it => - "/" + it.asPath.toList.map(_._1.value).mkString("/") - } getOrElse "/" + (Seq(it.module) ++ it.asPath.toList.map(_._1.value)).mkString("/") + } getOrElse state.circuit.main def getRelativePath(root: Option[InstanceTarget], inst: Option[IsComponent]): String = { val rootPath = root.map(_.asPath).getOrElse(Seq()) @@ -40,7 +40,7 @@ class GenerateFloorplanIRPass extends Transform with RegisteredTransform with De case x: InstanceTarget => pathStr case x: ReferenceTarget => pathStr + "." + x.ref case _ => ??? // Shouldn't exist - }).getOrElse("") + }) getOrElse "" } def newRecord(path: String, ref: Option[String], ofModule: Option[String], anno: FloorplanAnnotation) = From df3fa480516ccbf238d40bc38264edfbd67f2199 Mon Sep 17 00:00:00 2001 From: John Wright Date: Sun, 18 Jul 2021 21:31:19 -0700 Subject: [PATCH 44/73] Temporary commit, this compiles but some implementations are intentionally missing (???) --- .../barstools/floorplan/Constraints.scala | 235 +++++++++++------- .../main/scala/barstools/floorplan/IR.scala | 132 ++++++++-- .../compiler/ConstraintPropagationPass.scala | 80 ++++++ .../floorplan/compiler/FloorplanTree.scala | 93 +++++-- .../barstools/floorplan/compiler/Pass.scala | 6 +- .../compiler/PropagationPasses.scala | 12 - .../compiler/ReplaceAbstractGroupsPass.scala | 42 ++++ .../compiler/ReplaceHierarchicalPass.scala | 106 ++++++-- .../compiler/ReplaceMemMacroArrayPass.scala | 12 + .../compiler/SidebandAnnotationPass.scala | 4 + .../scala/barstools/floorplan/package.scala | 2 + 11 files changed, 563 insertions(+), 161 deletions(-) create mode 100644 floorplan/src/main/scala/barstools/floorplan/compiler/ConstraintPropagationPass.scala delete mode 100644 floorplan/src/main/scala/barstools/floorplan/compiler/PropagationPasses.scala create mode 100644 floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceAbstractGroupsPass.scala create mode 100644 floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceMemMacroArrayPass.scala diff --git a/floorplan/src/main/scala/barstools/floorplan/Constraints.scala b/floorplan/src/main/scala/barstools/floorplan/Constraints.scala index e924aabe..0a2c88c2 100644 --- a/floorplan/src/main/scala/barstools/floorplan/Constraints.scala +++ b/floorplan/src/main/scala/barstools/floorplan/Constraints.scala @@ -7,9 +7,7 @@ sealed trait Constraint { def and(that: Constraint): Constraint } -sealed abstract class PrimitiveConstraint extends Constraint - -final class Unconstrained extends PrimitiveConstraint { +final class Unconstrained extends Constraint { def and(that: Constraint) = that } @@ -18,106 +16,175 @@ object Unconstrained { def apply() = u } -final case class EqualTo(value: BigDecimal) extends PrimitiveConstraint { - def and(that: Constraint) = that match { - case that: EqualTo => if (this.value == that.value) this else ImpossibleConstraint(this, that) - case that: GreaterThanOrEqualTo => if (this.value >= that.value) this else ImpossibleConstraint(this, that) - case that: LessThanOrEqualTo => if (this.value <= that.value) this else ImpossibleConstraint(this, that) - case that: MultipleOf => if ((this.value % that.value) == BigDecimal(0)) this else ImpossibleConstraint(this, that) - case that: ImpossibleConstraint => that - case that: Unconstrained => this - case that => AndConstraint(this, that) - } -} - -final case class GreaterThanOrEqualTo(value: BigDecimal) extends PrimitiveConstraint { - def and(that: Constraint) = that match { - case that: EqualTo => if (this.value <= that.value) that else ImpossibleConstraint(this, that) - case that: GreaterThanOrEqualTo => if (this.value >= that.value) this else that - case that: LessThanOrEqualTo => if (this.value < that.value) AndConstraint(this, that) else - if (this.value == that.value) EqualTo(this.value) else ImpossibleConstraint(this, that) - case that: ImpossibleConstraint => that - case that: Unconstrained => this - case that => AndConstraint(this, that) - } +// TODO add a reason? +final class Impossible extends Constraint { + def and(that: Constraint) = this } -final case class LessThanOrEqualTo(value: BigDecimal) extends PrimitiveConstraint { - def and(that: Constraint) = that match { - case that: EqualTo => if (this.value >= that.value) that else ImpossibleConstraint(this, that) - case that: GreaterThanOrEqualTo => if (this.value > that.value) AndConstraint(this, that) else - if (this.value == that.value) EqualTo(this.value) else ImpossibleConstraint(this, that) - case that: LessThanOrEqualTo => if (this.value <= that.value) this else that - case that: ImpossibleConstraint => that - case that: Unconstrained => this - case that => AndConstraint(this, that) - } +object Impossible { + private val i = new Impossible + def apply() = i } -// TODO allow offset -final case class MultipleOf(value: BigDecimal) extends PrimitiveConstraint { - def and(that: Constraint) = that match { - case that: EqualTo => if ((that.value % this.value) == BigDecimal(0)) that else ImpossibleConstraint(this, that) - case that: MultipleOf => MultipleOf(lcm(this.value,that.value)) - case that: LessThanOrEqualTo => if (that.value < this.value) ImpossibleConstraint(this, that) else AndConstraint(this, that) - case that: ImpossibleConstraint => that - case that: Unconstrained => this - case that => AndConstraint(this, that) +final case class Constrained( + eq: Option[BigDecimal], + geq: Option[BigDecimal], + leq: Option[BigDecimal], + mof: Option[BigDecimal] +) extends Constraint { + + def and(that: Constraint): Constraint = { + that match { + case that: Unconstrained => this + case that: Impossible => that + case that: Constrained => + + // Combine raw constraints + val newMof = if (this.mof.isDefined && that.mof.isDefined) { + Some(lcm(this.mof.get, that.mof.get)) + } else { + this.mof.orElse(that.mof) + } + + val newLeq = if (this.leq.isDefined && that.leq.isDefined) { + Some(Seq(this.leq.get, that.leq.get).min) + } else { + this.leq.orElse(that.leq) + } + + val newGeq = if (this.geq.isDefined && that.geq.isDefined) { + Some(Seq(this.geq.get, that.geq.get).max) + } else { + this.geq.orElse(that.geq) + } + + if (this.eq.isDefined && that.eq.isDefined && (this.eq != that.eq)) { + return Impossible() + } + val newEq = this.eq.orElse(that.eq) + + Constrained(eq=newEq, geq=newGeq, leq=newLeq, mof=newMof).minimize + case _ => ??? + } } -} -sealed abstract class AggregateConstraint extends Constraint + def minimize: Constraint = { + // Check range on LEQ/GEQ + val newEq = if (leq.isDefined && geq.isDefined) { + if (leq.get < geq.get) { + return Impossible() + } else if (leq.get == geq.get) { + if (eq.isDefined && (eq.get != leq.get)) { + return Impossible() + } + leq + } else { + eq + } + } else { + eq + } -final case class ImpossibleConstraint(a: Constraint, b: Constraint) extends AggregateConstraint { - def and(that: Constraint) = this -} + // Check multiples + if (eq.isDefined && mof.isDefined && (eq.get % mof.get != BigDecimal(0))) { + return Impossible() + } -final case class AndConstraint(constraints: Seq[Constraint]) extends AggregateConstraint { - def and(that: Constraint) = that match { - case that: ImpossibleConstraint => that - case that: Unconstrained => this - case that => AndConstraint(this, that) + if (eq.isDefined) { + if (leq.isDefined) { + if (eq.get > leq.get) { + return Impossible() + } + } + if (geq.isDefined) { + if (eq.get < geq.get) { + return Impossible() + } + } + } + // TODO check if there exists a multiple in range + this.copy(eq=newEq) } - def flatten: AndConstraint = { - AndConstraint(constraints.collect({ - case x: AndConstraint => x.flatten.constraints - case x: PrimitiveConstraint => Seq(x) - }).reduce(_ ++ _)) + def +(that: Constraint): Constraint = { + that match { + case that: Unconstrained => this + case that: Impossible => that + case that: Constrained => + + // Combine raw constraints + val newMof = if (this.mof == that.mof) { + this.mof + } else { + None + } + + val newLeq = if (this.leq.isDefined && that.leq.isDefined) { + Some(this.leq.get + that.leq.get) + } else { + None + } + + val newGeq = if (this.geq.isDefined && that.geq.isDefined) { + Some(this.geq.get + that.geq.get) + } else { + None + } + + val newEq = if (this.eq.isDefined && that.eq.isDefined) { + Some(this.eq.get + that.eq.get) + } else { + None + } + + Constrained(eq=newEq, geq=newGeq, leq=newLeq, mof=newMof).minimize + case _ => ??? + } } +} - def reduce: Constraint = { - val flat = this.flatten.constraints +object EqualTo { + def apply(value: BigDecimal) = Constrained(Some(value), None, None, None) +} + +object GreaterThanOrEqualTo { + def apply(value: BigDecimal) = Constrained(None, Some(value), None, None) +} - val exact = flat.collect({ - case x:EqualTo => x - }).reduceOption[Constraint](_.and(_)) +object LessThanOrEqualTo { + def apply(value: BigDecimal) = Constrained(None, None, Some(value), None) +} - val gt = flat.collect({ - case x:GreaterThanOrEqualTo => x - }).reduceOption[Constraint](_.and(_)) +object MultipleOf { + def apply(value: BigDecimal) = Constrained(None, None, None, Some(value)) +} - val lt = flat.collect({ - case x:LessThanOrEqualTo => x - }).reduceOption[Constraint](_.and(_)) +case class Constraints( + width: Constraint = Unconstrained(), + height: Constraint = Unconstrained(), + area: Constraint = Unconstrained(), + aspectRatio: Constraint = Unconstrained() +) + +object Constraints { + + def sized(w: BigDecimal, h: BigDecimal): Constraints = { + Constraints( + EqualTo(w), + EqualTo(h), + Unconstrained(), + Unconstrained() + ) + } - val mult = flat.collect({ - case x:MultipleOf => x - }).reduceOption[Constraint](_.and(_)) + def applyConstraints[T <: Element](e: T, c: Constraints): T = { + ??? + } - // if exact is defined, we'll either be exact or impossible - exact.map({ value => - val postGt = gt.map(_.and(value)).getOrElse(value) - val postLt = lt.map(_.and(postGt)).getOrElse(postGt) - mult.map(_.and(postLt)).getOrElse(postLt) - }).getOrElse { - ??? - } + def applyConstraintsUp[T <: Element](e: T, c: Constraints, idx: Int): T = { + ??? } -} -object AndConstraint { - def apply(a: Constraint, b: Constraint): AndConstraint = AndConstraint(Seq(a, b)) } sealed trait PlacementAnchor diff --git a/floorplan/src/main/scala/barstools/floorplan/IR.scala b/floorplan/src/main/scala/barstools/floorplan/IR.scala index 35827051..3af8b69e 100644 --- a/floorplan/src/main/scala/barstools/floorplan/IR.scala +++ b/floorplan/src/main/scala/barstools/floorplan/IR.scala @@ -14,19 +14,30 @@ sealed abstract class Element { def name: String def level: Int def serialize = FloorplanSerialization.serialize(this) + + def flatIndexOf(s: String): Int + + def mapNames(m: (String) => String): Element } sealed abstract class ElementWithParent extends Element { def parent: String } -sealed abstract class Primitive extends ElementWithParent +sealed abstract class Primitive extends ElementWithParent { + def flatIndexOf(s: String): Int = -1 +} sealed abstract class Group extends ElementWithParent { def elements: Seq[Option[String]] + + def flatIndexOf(s: String) = elements.indexOf(Some(s)) } -sealed abstract class Top extends Element +sealed abstract class Top extends Element { + def topGroup: String + def flatIndexOf(s: String): Int = if (topGroup == s) 0 else -1 +} ////////////////////////////////////////////// Hierarchical barrier @@ -35,6 +46,7 @@ private[floorplan] final case class HierarchicalBarrier( parent: String ) extends Primitive { final def level = 3 + def mapNames(m: (String) => String): Element = this.copy(name = m(name), parent = m(parent)) } ////////////////////////////////////////////// Rectangular things @@ -63,7 +75,7 @@ object IRLevel { } sealed abstract class AbstractRectPrimitive extends Primitive { - final def level = 2 + final def level = 4 } sealed abstract class ConstrainedRectPrimitive extends Primitive with ConstrainedRectLike { @@ -85,7 +97,9 @@ private[floorplan] final case class ConstrainedSpacerRect( height: Constraint = Unconstrained(), area: Constraint = Unconstrained(), aspectRatio: Constraint = Unconstrained() -) extends ConstrainedRectPrimitive +) extends ConstrainedRectPrimitive { + def mapNames(m: (String) => String): Element = this.copy(name = m(name), parent = m(parent)) +} private[floorplan] final case class SizedSpacerRect( name: String, @@ -94,7 +108,9 @@ private[floorplan] final case class SizedSpacerRect( y: BigDecimal, width: BigDecimal, height: BigDecimal -) extends SizedRectPrimitive +) extends SizedRectPrimitive { + def mapNames(m: (String) => String): Element = this.copy(name = m(name), parent = m(parent)) +} // No PlacedSpacerRect exists because they're only for spacing @@ -114,7 +130,9 @@ private[floorplan] final case class ConstrainedLogicRect( area: Constraint, aspectRatio: Constraint, hardBoundary: Boolean -) extends ConstrainedRectPrimitive +) extends ConstrainedRectPrimitive { + def mapNames(m: (String) => String): Element = this.copy(name = m(name), parent = m(parent)) +} private[floorplan] final case class SizedLogicRect( name: String, @@ -122,7 +140,9 @@ private[floorplan] final case class SizedLogicRect( width: BigDecimal, height: BigDecimal, hardBoundary: Boolean -) extends SizedRectPrimitive +) extends SizedRectPrimitive { + def mapNames(m: (String) => String): Element = this.copy(name = m(name), parent = m(parent)) +} private[floorplan] final case class PlacedLogicRect( name: String, @@ -132,7 +152,9 @@ private[floorplan] final case class PlacedLogicRect( width: BigDecimal, height: BigDecimal, hardBoundary: Boolean -) extends PlacedRectPrimitive +) extends PlacedRectPrimitive { + def mapNames(m: (String) => String): Element = this.copy(name = m(name), parent = m(parent)) +} private[floorplan] final case class ConstrainedHierarchicalTop( @@ -146,6 +168,7 @@ private[floorplan] final case class ConstrainedHierarchicalTop( hardBoundary: Boolean ) extends Top with ConstrainedRectLike { final def level = 3 + def mapNames(m: (String) => String): Element = this.copy(name = m(name), topGroup = m(topGroup)) } private[floorplan] final case class PlacedHierarchicalTop( @@ -159,6 +182,7 @@ private[floorplan] final case class PlacedHierarchicalTop( final def x = BigDecimal(0) final def y = BigDecimal(0) final def level = 0 + def mapNames(m: (String) => String): Element = this.copy(name = m(name), topGroup = m(topGroup)) } ////////////////////////////////////////////// Aggregate (Group) things @@ -172,6 +196,13 @@ sealed abstract class Grid extends Group { assert(yDim > 0, "Y dimension of grid must be positive") def get(x: Int, y: Int) = elements(xDim*y + x) + + def indexOf(s: String): Option[(Int, Int)] = { + val idx = elements.indexOf(Some(s)) + val x = idx % xDim + val y = idx / xDim + if (idx == -1) None else Some((x, y)) + } } // TODO eventually rename this to AbstractWeightedGrid @@ -184,7 +215,14 @@ private[floorplan] final case class WeightedGrid( weights: Seq[BigDecimal], packed: Boolean ) extends Grid { - def level = 2 + def level = 3 + def mapNames(m: (String) => String): Element = { + this.copy( + name = m(name), + parent = m(parent), + elements = elements.map(_.map(m)) + ) + } } private[floorplan] final case class ConstrainedWeightedGrid( @@ -195,12 +233,19 @@ private[floorplan] final case class ConstrainedWeightedGrid( elements: Seq[Option[String]], weights: Seq[BigDecimal], packed: Boolean, - width: Constraint = Unconstrained(), - height: Constraint = Unconstrained(), - area: Constraint = Unconstrained(), - aspectRatio: Constraint = Unconstrained() + width: Seq[Constraint], + height: Seq[Constraint], + area: Seq[Constraint], + aspectRatio: Seq[Constraint] ) extends Grid { def level = 2 + def mapNames(m: (String) => String): Element = { + this.copy( + name = m(name), + parent = m(parent), + elements = elements.map(_.map(m)) + ) + } } // TODO eventually rename this to AbstractElasticGrid @@ -211,7 +256,14 @@ private[floorplan] final case class ElasticGrid( yDim: Int, elements: Seq[Option[String]] ) extends Grid { - def level = 2 + def level = 3 + def mapNames(m: (String) => String): Element = { + this.copy( + name = m(name), + parent = m(parent), + elements = elements.map(_.map(m)) + ) + } } private[floorplan] final case class ConstrainedElasticGrid( @@ -220,12 +272,19 @@ private[floorplan] final case class ConstrainedElasticGrid( xDim: Int, yDim: Int, elements: Seq[Option[String]], - width: Constraint = Unconstrained(), - height: Constraint = Unconstrained(), - area: Constraint = Unconstrained(), - aspectRatio: Constraint = Unconstrained() + width: Seq[Constraint], + height: Seq[Constraint], + area: Seq[Constraint], + aspectRatio: Seq[Constraint] ) extends Grid { def level = 2 + def mapNames(m: (String) => String): Element = { + this.copy( + name = m(name), + parent = m(parent), + elements = elements.map(_.map(m)) + ) + } } private[floorplan] final case class SizedGrid( @@ -238,6 +297,13 @@ private[floorplan] final case class SizedGrid( heights: Seq[BigDecimal] ) extends Grid { def level = 1 + def mapNames(m: (String) => String): Element = { + this.copy( + name = m(name), + parent = m(parent), + elements = elements.map(_.map(m)) + ) + } } @@ -247,7 +313,9 @@ private[floorplan] final case class SizedGrid( private[floorplan] final case class MemElement( name: String, parent: String -) extends AbstractRectPrimitive +) extends AbstractRectPrimitive { + def mapNames(m: (String) => String): Element = this.copy(name = m(name), parent = m(parent)) +} // Container for MemElements private[floorplan] final case class MemElementArray( @@ -260,6 +328,13 @@ private[floorplan] final case class MemElementArray( aspectRatio: Constraint = Unconstrained() ) extends Group with ConstrainedRectLike { def level = 4 + def mapNames(m: (String) => String): Element = { + this.copy( + name = m(name), + parent = m(parent), + elements = elements.map(_.map(m)) + ) + } } // Container for MemElements that have been converted to Macros @@ -276,6 +351,13 @@ private[floorplan] final case class MemMacroArray( aspectRatio: Constraint = Unconstrained() ) extends Group with ConstrainedRectLike { def level = 2 + def mapNames(m: (String) => String): Element = { + this.copy( + name = m(name), + parent = m(parent), + elements = elements.map(_.map(m)) + ) + } } // Reference to a macro blackbox with unknown dimensions @@ -283,7 +365,9 @@ private[floorplan] final case class MemMacroArray( private[floorplan] final case class AbstractMacro ( name: String, parent: String -) extends AbstractRectPrimitive +) extends AbstractRectPrimitive { + def mapNames(m: (String) => String): Element = this.copy(name = m(name), parent = m(parent)) +} // Reference to a macro blackbox that has known dimensions private[floorplan] final case class SizedMacro ( @@ -291,7 +375,9 @@ private[floorplan] final case class SizedMacro ( parent: String, width: BigDecimal, height: BigDecimal -) extends SizedRectPrimitive +) extends SizedRectPrimitive { + def mapNames(m: (String) => String): Element = this.copy(name = m(name), parent = m(parent)) +} // Reference to a macro blackbox that has known dimensions and has been placed private[floorplan] final case class PlacedMacro ( @@ -301,5 +387,7 @@ private[floorplan] final case class PlacedMacro ( y: BigDecimal, width: BigDecimal, height: BigDecimal -) extends PlacedRectPrimitive +) extends PlacedRectPrimitive { + def mapNames(m: (String) => String): Element = this.copy(name = m(name), parent = m(parent)) +} diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/ConstraintPropagationPass.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/ConstraintPropagationPass.scala new file mode 100644 index 00000000..dc90043f --- /dev/null +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/ConstraintPropagationPass.scala @@ -0,0 +1,80 @@ +// See LICENSE for license details +package barstools.floorplan.compiler + +import barstools.floorplan._ + +class ConstraintPropagationPass(val topMod: String) extends Pass { + def execute(state: FloorplanState): FloorplanState = { + val tree = new FloorplanTree(state, topMod) + + import Constraints._ + + // TODO This probably should use a SAT solver or something fancier + + // Top-down pass + tree.traverseMapPre { node => + val constraints: Constraints = node.parent.map(_.record.element match { + case e: ConstrainedHierarchicalTop => + Constraints(e.width, e.height, e.area, e.aspectRatio) + case e: PlacedHierarchicalTop => + Constraints.sized(e.width, e.height) + case e: ConstrainedWeightedGrid => + ??? // TODO + case e: ConstrainedElasticGrid => + ??? // TODO + case e: SizedGrid => + val (x, y) = e.indexOf(node.record.element.name).get + Constraints.sized(e.widths(x), e.heights(y)) + case e: MemMacroArray => + Constraints() // These *should* be hard macros at this point, so no need to constrain them + case _ => ??? // Many types can never be parents and shouldn't get here + }).getOrElse(Constraints()) + // Only modify child + (None, Some(node.record.copy(element = applyConstraints(node.record.element, constraints)))) + } + + // Bottom-up pass + tree.traverseMapPost { node => + // Get idx in parent + val idx = node.parent.map(_.record.element.flatIndexOf(node.record.element.name)).getOrElse(-1) + + val constraints: Constraints = node.record.element match { + case e: ConstrainedSpacerRect => + Constraints(e.width, e.height, e.area, e.aspectRatio) + case e: SizedSpacerRect => + Constraints.sized(e.width, e.height) + case e: ConstrainedLogicRect => + Constraints(e.width, e.height, e.area, e.aspectRatio) + case e: SizedLogicRect => + Constraints.sized(e.width, e.height) + case e: PlacedLogicRect => + Constraints.sized(e.width, e.height) + case e: ConstrainedHierarchicalTop => + Constraints(e.width, e.height, e.area, e.aspectRatio) + case e: PlacedHierarchicalTop => + Constraints.sized(e.width, e.height) + case e: ConstrainedWeightedGrid => + ??? // TODO + case e: ConstrainedElasticGrid => + ??? // TODO + case e: SizedGrid => + Constraints.sized(e.widths.sum, e.heights.sum) + case e: MemMacroArray => + Constraints(e.width, e.height, e.area, e.aspectRatio) + case e: SizedMacro => + Constraints.sized(e.width, e.height) + case e: PlacedMacro => + Constraints.sized(e.width, e.height) + case _ => ??? + } + + // Only modify parent + (Some(node.record.copy(element = applyConstraintsUp(node.record.element, constraints, idx))), None) + } + + // TODO propagate constraints to siblings + + //tree.toState + state + } +} diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanTree.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanTree.scala index d6aaa231..fa5c00a8 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanTree.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanTree.scala @@ -1,31 +1,92 @@ // See LICENSE for license details package barstools.floorplan.compiler -import scala.collection.mutable.{HashMap} -import barstools.floorplan.{Element, FloorplanState, FloorplanElementRecord} +import scala.collection.mutable.{ArrayBuffer, HashMap} +import barstools.floorplan._ -/* +class FloorplanTree(val state: FloorplanState, val topMod: String) { -class FloorplanTree(state: FloorplanState, val topMod: String) { + val allNodes = new HashMap[String, Node]() - assert(state.level < 3, "Cannot have Hierarchicals") + class Node(val parent: Option[Node], initialRecord: FloorplanElementRecord) { + val children = new ArrayBuffer[Node]() - class Node(val parent: Option[Node], val record: FloorplanElementRecord) { - val children = new HashMap[String, Node]() - val name = record.element.name + // TODO this might be dangerous + private var _record = initialRecord - protected[FloorplanTree] def addNode(node: Node) { - children =+ (node.name -> node) + def record = _record + + def addChildRecord(cRecord: FloorplanElementRecord): Node = { + val n = new Node(Some(this), cRecord) + children += n + allNodes += (cRecord.element.name -> n) + n } - protected[FloorplanTree] def addRecord(record: FloorplanElementRecord) { addNode(new Node(Some(this), record)) } + def replace(r: FloorplanElementRecord) { _record = r } } + val allRecords: Map[String, FloorplanElementRecord] = state.records.map({ x => (x.element.name -> x) }).toMap -} + def getRecord(s: String): FloorplanElementRecord = allRecords(s) + def getNode(s: String): Node = allNodes(s) -object FloorplanTree { - def apply(state: FloorplanState, topMod: String) = new FloorplanTree(state, topMod) -} + val topRecords = state.records.flatMap({ r => r.element match { + case e: Top => Seq(r) + case _ => Seq() + }}) + assert(topRecords.length == 1, "Must be exactly one Top record") + val topRecord = topRecords(0) + + + private def dfs(parent: Option[Node], r: FloorplanElementRecord): Node = { + r.element match { + case e: Top => + assert(!parent.isDefined, "Cannot have multiple tops") + val n = new Node(None, r) + dfs(Some(n), getRecord(e.topGroup)) + n + case e: Primitive => + assert(parent.isDefined, "Must have parent") + parent.get.addChildRecord(r) + case e: Group => + assert(parent.isDefined, "Must have parent") + val n = parent.get.addChildRecord(r) + e.elements.foreach(_.foreach(x => dfs(Some(n), getRecord(x)))) + n + case _ => ??? + } + } + + val topNode = dfs(None, topRecord) -*/ + // Traverse using DFS, passing the node to a function which expects an + // (Option[FloorplanElementRecord], Option[FloorplanElementRecord]) return + // (None, None) = do no modify + // (None, Some(record)) = modify node + // (Some(record), None) = modify parent + // (Some(r1), Some(r2)) = modify both + def traverseMapPre(f: (Node => (Option[FloorplanElementRecord], Option[FloorplanElementRecord]))) { traverseMapPreHelper(topNode, f) } + def traverseMapPost(f: (Node => (Option[FloorplanElementRecord], Option[FloorplanElementRecord]))) { traverseMapPostHelper(topNode, f) } + + private def traverseMapPreHelper(n: Node, f: (Node => (Option[FloorplanElementRecord], Option[FloorplanElementRecord]))) { + val (parent, child) = f(n) + parent.foreach { r => n.parent.foreach(_.replace(r)) } + child.foreach { r => n.replace(r) } + n.children.foreach { c => traverseMapPreHelper(c, f) } + } + + private def traverseMapPostHelper(n: Node, f: (Node => (Option[FloorplanElementRecord], Option[FloorplanElementRecord]))) { + n.children.foreach { c => traverseMapPostHelper(c, f) } + val (parent, child) = f(n) + parent.foreach { r => n.parent.foreach(_.replace(r)) } + child.foreach { r => n.replace(r) } + } + + def toState: FloorplanState = { + val records = allRecords.values.toSeq + val level = records.map(_.element.level).max + FloorplanState(records, level) + } + +} diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/Pass.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/Pass.scala index d0077c98..77a8df84 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/Pass.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/Pass.scala @@ -11,9 +11,10 @@ object Pass { Seq( new TransformMemsPass(instMap), new SidebandAnnotationPass(sbAnnos), + new ReplaceAbstractGroupsPass, new ReplaceHierarchicalPass(opts.topMod), - new TopDownPropagationPass(opts.topMod), - new BottomUpPropagationPass(opts.topMod), + new ConstraintPropagationPass(opts.topMod), + new ReplaceMemMacroArrayPass, new ResolveConstraintsPass, new CalculatePlacementsPass ) @@ -31,3 +32,4 @@ class ResolveConstraintsPass extends Pass { class CalculatePlacementsPass extends Pass { def execute(state: FloorplanState): FloorplanState = state // TODO } + diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/PropagationPasses.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/PropagationPasses.scala deleted file mode 100644 index 533e2324..00000000 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/PropagationPasses.scala +++ /dev/null @@ -1,12 +0,0 @@ -// See LICENSE for license details -package barstools.floorplan.compiler - -import barstools.floorplan._ - -class TopDownPropagationPass(val topMod: String) extends Pass { - def execute(state: FloorplanState): FloorplanState = state // TODO -} - -class BottomUpPropagationPass(val topMod: String) extends Pass { - def execute(state: FloorplanState): FloorplanState = state // TODO -} diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceAbstractGroupsPass.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceAbstractGroupsPass.scala new file mode 100644 index 00000000..97d5dea9 --- /dev/null +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceAbstractGroupsPass.scala @@ -0,0 +1,42 @@ +// See LICENSE for license details +package barstools.floorplan.compiler + +import barstools.floorplan._ + +class ReplaceAbstractGroupsPass extends Pass { + def execute(state: FloorplanState): FloorplanState = { + val newRecords = state.records.map { record => + val newElement = record.element match { + case e: WeightedGrid => + Some(ConstrainedWeightedGrid( + name = e.name, + parent = e.parent, + xDim = e.xDim, + yDim = e.yDim, + elements = e.elements, + weights = e.weights, + packed = e.packed, + width = Seq.fill(e.xDim*e.yDim) { Unconstrained() }, + height = Seq.fill(e.xDim*e.yDim) { Unconstrained() }, + area = Seq.fill(e.xDim*e.yDim) { Unconstrained() }, + aspectRatio = Seq.fill(e.xDim*e.yDim) { Unconstrained() } + )) + case e: ElasticGrid => + Some(ConstrainedElasticGrid( + name = e.name, + parent = e.parent, + xDim = e.xDim, + yDim = e.yDim, + elements = e.elements, + width = Seq.fill(e.xDim*e.yDim) { Unconstrained() }, + height = Seq.fill(e.xDim*e.yDim) { Unconstrained() }, + area = Seq.fill(e.xDim*e.yDim) { Unconstrained() }, + aspectRatio = Seq.fill(e.xDim*e.yDim) { Unconstrained() } + )) + case _ => None + } + newElement.map(e => record.copy(element = e)).getOrElse(record) + } + state.copy(records = newRecords) + } +} diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceHierarchicalPass.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceHierarchicalPass.scala index 719af657..94dc2d9e 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceHierarchicalPass.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceHierarchicalPass.scala @@ -1,7 +1,7 @@ // See LICENSE for license details package barstools.floorplan.compiler -import scala.collection.mutable.{HashMap} +import scala.collection.mutable.{HashMap, HashSet} import barstools.floorplan._ class ReplaceHierarchicalPass(val topMod: String) extends Pass { @@ -17,44 +17,100 @@ class ReplaceHierarchicalPass(val topMod: String) extends Pass { case _ => None })).toMap - // TODO we need to also change the root path for all elements underneath these + val root = topMaps(topMod).root + val rootNameSet = HashSet(state.records.filter(_.root == root).map(_.element.name):_*) + + val nonRootPaths = topMaps.filterKeys(_ != topMod).values.map(_.root) + + def getUniqueName(suggestion: String): String = { + var i = 0 + var tmp = suggestion + s"_${i}" + while (rootNameSet.contains(tmp)) { + i = i + 1 + tmp = suggestion + s"_${i}" + } + rootNameSet += tmp + tmp + } + + val renameMap = new HashMap[(String, String), String]() + + def rename(oldRoot: String, oldName: String): String = { + if (oldRoot == rootNameSet) { return oldName } + val tup = (oldRoot, oldName) + if (renameMap.contains(tup)) { return renameMap(tup) } + val newName = getUniqueName(oldName) + renameMap += (tup -> newName) + newName + } + + def getRelPath(fullPath: Option[String]): Option[String] = fullPath.map { p => + assert(p.startsWith(root), "Full path must be in root scope") + p.substring(root.length) + } + val newRecords = state.records.flatMap({r => r.element match { case e: HierarchicalBarrier => assert(topMaps.contains(r.fullPath), s"All HierarchicalBarriers must have a corresponding HierarchicalTop: ${r.fullPath} is missing") - val newE = (topMaps(r.fullPath).element match { + val tr = topMaps(r.fullPath) + (tr.element match { case t: ConstrainedHierarchicalTop => - ConstrainedElasticGrid( - name = t.name, - parent = e.parent, + // We replace with two elements (one to replace each name); they'll get optimized away later + // Parent is first (replaces e), child is after (replaces t) + Seq(ConstrainedElasticGrid( + name = rename(r.root, e.name), + parent = rename(r.root, e.parent), xDim = 1, yDim = 1, - elements = Seq(Some(t.topGroup)), - width = t.width, - height = t.height, - area = t.area, - aspectRatio = t.aspectRatio - ) + elements = Seq(Some(rename(tr.root, t.name))), + width = Seq(t.width), + height = Seq(t.height), + area = Seq(t.area), + aspectRatio = Seq(t.aspectRatio) + ), ConstrainedElasticGrid( + name = rename(tr.root, t.name), + parent = rename(r.root, e.name), + xDim = 1, + yDim = 1, + elements = Seq(Some(rename(tr.root, t.topGroup))), + width = Seq(t.width), + height = Seq(t.height), + area = Seq(t.area), + aspectRatio = Seq(t.aspectRatio) + )) case t: PlacedHierarchicalTop => - SizedGrid( - name = t.name, - parent = e.parent, + Seq(SizedGrid( + name = rename(r.root, e.name), + parent = rename(r.root, e.parent), + xDim = 1, + yDim = 1, + elements = Seq(Some(rename(tr.root, t.name))), + widths = Seq(t.width), + heights = Seq(t.height) + ), SizedGrid( + name = rename(tr.root, t.name), + parent = rename(r.root, e.name), xDim = 1, yDim = 1, - elements = Seq(Some(t.topGroup)), + elements = Seq(Some(rename(tr.root, t.topGroup))), widths = Seq(t.width), heights = Seq(t.height) - ) + )) case _ => ??? - }) - Seq(FloorplanElementRecord( - root = r.root, // TODO clean up path - inst = r.inst, - ofModule = r.ofModule, - element = newE - )) + }).map(newE => FloorplanElementRecord( + root = root, + inst = getRelPath(r.inst.map(_ => r.fullPath)), + ofModule = r.ofModule, + element = newE + )) case e: ConstrainedHierarchicalTop if r.ofModule != Some(topMod) => Seq() case e: PlacedHierarchicalTop if r.ofModule != Some(topMod) => Seq() - case e => Seq(r) + case e => Seq(r.copy( + root = root, + inst = getRelPath(r.inst.map(_ => r.fullPath)), + ofModule = r.ofModule, + element = r.element.mapNames(x => rename(r.root, x)) + )) }}) state.copy(records = newRecords, level = 2) // TODO recalculate level } diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceMemMacroArrayPass.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceMemMacroArrayPass.scala new file mode 100644 index 00000000..b3d7537a --- /dev/null +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceMemMacroArrayPass.scala @@ -0,0 +1,12 @@ +// See LICENSE for license details +package barstools.floorplan.compiler + +import barstools.floorplan._ + +class ReplaceMemMacroArrayPass extends Pass { + def execute(state: FloorplanState): FloorplanState = { + // TODO this needs some additional options, etc. + // For the proof-of-concept, we'll just replace them with an Array that meets whatever box constraints + state // TODO + } +} diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/SidebandAnnotationPass.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/SidebandAnnotationPass.scala index 0dce3131..c7668d81 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/SidebandAnnotationPass.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/SidebandAnnotationPass.scala @@ -76,6 +76,10 @@ class SidebandAnnotationPass(val sbMap: Map[String, SidebandAnnotation]) extends } record.copy(element = newElement) } + newRecords.map(_.element).collectFirst { + case e: AbstractMacro => + throw new Exception("Unannoated macros still exist after SidebandAnnotationPass") + } state.copy(records = newRecords) } } diff --git a/floorplan/src/main/scala/barstools/floorplan/package.scala b/floorplan/src/main/scala/barstools/floorplan/package.scala index b5d540da..e9ce1c68 100644 --- a/floorplan/src/main/scala/barstools/floorplan/package.scala +++ b/floorplan/src/main/scala/barstools/floorplan/package.scala @@ -13,4 +13,6 @@ def gcd(a: BigDecimal, b: BigDecimal): BigDecimal = { def lcm(a: BigDecimal, b: BigDecimal): BigDecimal = (a*b)/gcd(a,b) +type PrimitiveConstraint = Constraint + } From 0947b05c5bc856f26b96a4ee492acf56277d64ff Mon Sep 17 00:00:00 2001 From: John Wright Date: Sun, 18 Jul 2021 22:24:47 -0700 Subject: [PATCH 45/73] Fix unimplemented methods --- .../barstools/floorplan/Constraints.scala | 12 +-- .../main/scala/barstools/floorplan/IR.scala | 99 ++++++++++++++++++- .../compiler/ConstraintPropagationPass.scala | 35 +++++-- 3 files changed, 122 insertions(+), 24 deletions(-) diff --git a/floorplan/src/main/scala/barstools/floorplan/Constraints.scala b/floorplan/src/main/scala/barstools/floorplan/Constraints.scala index 0a2c88c2..00f5e356 100644 --- a/floorplan/src/main/scala/barstools/floorplan/Constraints.scala +++ b/floorplan/src/main/scala/barstools/floorplan/Constraints.scala @@ -5,10 +5,12 @@ import scala.math.{BigInt, BigDecimal} sealed trait Constraint { def and(that: Constraint): Constraint + def +(that: Constraint): Constraint } final class Unconstrained extends Constraint { def and(that: Constraint) = that + def +(that: Constraint) = that } object Unconstrained { @@ -19,6 +21,7 @@ object Unconstrained { // TODO add a reason? final class Impossible extends Constraint { def and(that: Constraint) = this + def +(that: Constraint) = this } object Impossible { @@ -176,15 +179,6 @@ object Constraints { Unconstrained() ) } - - def applyConstraints[T <: Element](e: T, c: Constraints): T = { - ??? - } - - def applyConstraintsUp[T <: Element](e: T, c: Constraints, idx: Int): T = { - ??? - } - } sealed trait PlacementAnchor diff --git a/floorplan/src/main/scala/barstools/floorplan/IR.scala b/floorplan/src/main/scala/barstools/floorplan/IR.scala index 3af8b69e..f7fcb8d4 100644 --- a/floorplan/src/main/scala/barstools/floorplan/IR.scala +++ b/floorplan/src/main/scala/barstools/floorplan/IR.scala @@ -17,6 +17,8 @@ sealed abstract class Element { def flatIndexOf(s: String): Int + def applyConstraints(c: Constraints): Element + def mapNames(m: (String) => String): Element } @@ -31,7 +33,7 @@ sealed abstract class Primitive extends ElementWithParent { sealed abstract class Group extends ElementWithParent { def elements: Seq[Option[String]] - def flatIndexOf(s: String) = elements.indexOf(Some(s)) + def flatIndexOf(s: String): Int = elements.indexOf(Some(s)) } sealed abstract class Top extends Element { @@ -47,6 +49,7 @@ private[floorplan] final case class HierarchicalBarrier( ) extends Primitive { final def level = 3 def mapNames(m: (String) => String): Element = this.copy(name = m(name), parent = m(parent)) + def applyConstraints(c: Constraints): Element = this } ////////////////////////////////////////////// Rectangular things @@ -99,6 +102,12 @@ private[floorplan] final case class ConstrainedSpacerRect( aspectRatio: Constraint = Unconstrained() ) extends ConstrainedRectPrimitive { def mapNames(m: (String) => String): Element = this.copy(name = m(name), parent = m(parent)) + def applyConstraints(c: Constraints): Element = this.copy( + width = width.and(c.width), + height = height.and(c.height), + area = area.and(c.area), + aspectRatio = aspectRatio.and(c.aspectRatio) + ) } private[floorplan] final case class SizedSpacerRect( @@ -110,6 +119,7 @@ private[floorplan] final case class SizedSpacerRect( height: BigDecimal ) extends SizedRectPrimitive { def mapNames(m: (String) => String): Element = this.copy(name = m(name), parent = m(parent)) + def applyConstraints(c: Constraints): Element = this } // No PlacedSpacerRect exists because they're only for spacing @@ -132,6 +142,12 @@ private[floorplan] final case class ConstrainedLogicRect( hardBoundary: Boolean ) extends ConstrainedRectPrimitive { def mapNames(m: (String) => String): Element = this.copy(name = m(name), parent = m(parent)) + def applyConstraints(c: Constraints): Element = this.copy( + width = width.and(c.width), + height = height.and(c.height), + area = area.and(c.area), + aspectRatio = aspectRatio.and(c.aspectRatio) + ) } private[floorplan] final case class SizedLogicRect( @@ -142,6 +158,7 @@ private[floorplan] final case class SizedLogicRect( hardBoundary: Boolean ) extends SizedRectPrimitive { def mapNames(m: (String) => String): Element = this.copy(name = m(name), parent = m(parent)) + def applyConstraints(c: Constraints): Element = this } private[floorplan] final case class PlacedLogicRect( @@ -154,6 +171,7 @@ private[floorplan] final case class PlacedLogicRect( hardBoundary: Boolean ) extends PlacedRectPrimitive { def mapNames(m: (String) => String): Element = this.copy(name = m(name), parent = m(parent)) + def applyConstraints(c: Constraints): Element = this } @@ -169,6 +187,12 @@ private[floorplan] final case class ConstrainedHierarchicalTop( ) extends Top with ConstrainedRectLike { final def level = 3 def mapNames(m: (String) => String): Element = this.copy(name = m(name), topGroup = m(topGroup)) + def applyConstraints(c: Constraints): Element = this.copy( + width = width.and(c.width), + height = height.and(c.height), + area = area.and(c.area), + aspectRatio = aspectRatio.and(c.aspectRatio) + ) } private[floorplan] final case class PlacedHierarchicalTop( @@ -183,6 +207,7 @@ private[floorplan] final case class PlacedHierarchicalTop( final def y = BigDecimal(0) final def level = 0 def mapNames(m: (String) => String): Element = this.copy(name = m(name), topGroup = m(topGroup)) + def applyConstraints(c: Constraints): Element = this } ////////////////////////////////////////////// Aggregate (Group) things @@ -195,14 +220,20 @@ sealed abstract class Grid extends Group { assert(xDim > 0, "X dimension of grid must be positive") assert(yDim > 0, "Y dimension of grid must be positive") - def get(x: Int, y: Int) = elements(xDim*y + x) + def toIdx(x: Int, y: Int): Int = xDim*y + x + def fromIdx(i: Int): (Int, Int) = (i % xDim, i / xDim) + + def get(x: Int, y: Int) = elements(toIdx(x, y)) def indexOf(s: String): Option[(Int, Int)] = { val idx = elements.indexOf(Some(s)) - val x = idx % xDim - val y = idx / xDim - if (idx == -1) None else Some((x, y)) + if (idx == -1) None else Some(fromIdx(idx)) } + + def applyConstraintsTo(c: Constraints, idx: Int): Element + def applyConstraintsTo(c: Constraints, xyo: Option[(Int, Int)]): Element = applyConstraintsTo(c, xyo.get) + def applyConstraintsTo(c: Constraints, xy: (Int, Int)): Element = applyConstraintsTo(c, xy._1, xy._2) + def applyConstraintsTo(c: Constraints, x: Int, y: Int): Element = applyConstraintsTo(c, toIdx(x, y)) } // TODO eventually rename this to AbstractWeightedGrid @@ -223,6 +254,20 @@ private[floorplan] final case class WeightedGrid( elements = elements.map(_.map(m)) ) } + def applyConstraints(c: Constraints): Element = this // TODO this is NOT correct + def applyConstraintsTo(c: Constraints, idx: Int): Element = ConstrainedWeightedGrid( + name = name, + parent = parent, + xDim = xDim, + yDim = yDim, + elements = elements, + weights = weights, + packed = packed, + width = (Seq.fill(xDim*yDim) { Unconstrained() }).updated(idx, c.width), + height = (Seq.fill(xDim*yDim) { Unconstrained() }).updated(idx, c.height), + area = (Seq.fill(xDim*yDim) { Unconstrained() }).updated(idx, c.area), + aspectRatio = (Seq.fill(xDim*yDim) { Unconstrained() }).updated(idx, c.aspectRatio) + ) } private[floorplan] final case class ConstrainedWeightedGrid( @@ -246,6 +291,13 @@ private[floorplan] final case class ConstrainedWeightedGrid( elements = elements.map(_.map(m)) ) } + def applyConstraints(c: Constraints): Element = this // TODO this is NOT correct + def applyConstraintsTo(c: Constraints, idx: Int): Element = this.copy( + width = width.updated(idx, width(idx).and(c.width)), + height = height.updated(idx, height(idx).and(c.height)), + area = area.updated(idx, area(idx).and(c.area)), + aspectRatio = aspectRatio.updated(idx, aspectRatio(idx).and(c.aspectRatio)) + ) } // TODO eventually rename this to AbstractElasticGrid @@ -264,6 +316,18 @@ private[floorplan] final case class ElasticGrid( elements = elements.map(_.map(m)) ) } + def applyConstraints(c: Constraints): Element = this // TODO this is NOT correct + def applyConstraintsTo(c: Constraints, idx: Int): Element = ConstrainedElasticGrid( + name = name, + parent = parent, + xDim = xDim, + yDim = yDim, + elements = elements, + width = (Seq.fill(xDim*yDim) { Unconstrained() }).updated(idx, c.width), + height = (Seq.fill(xDim*yDim) { Unconstrained() }).updated(idx, c.height), + area = (Seq.fill(xDim*yDim) { Unconstrained() }).updated(idx, c.area), + aspectRatio = (Seq.fill(xDim*yDim) { Unconstrained() }).updated(idx, c.aspectRatio) + ) } private[floorplan] final case class ConstrainedElasticGrid( @@ -285,6 +349,13 @@ private[floorplan] final case class ConstrainedElasticGrid( elements = elements.map(_.map(m)) ) } + def applyConstraints(c: Constraints): Element = this // TODO this is NOT correct + def applyConstraintsTo(c: Constraints, idx: Int): Element = this.copy( + width = width.updated(idx, width(idx).and(c.width)), + height = height.updated(idx, height(idx).and(c.height)), + area = area.updated(idx, area(idx).and(c.area)), + aspectRatio = aspectRatio.updated(idx, aspectRatio(idx).and(c.aspectRatio)) + ) } private[floorplan] final case class SizedGrid( @@ -304,6 +375,8 @@ private[floorplan] final case class SizedGrid( elements = elements.map(_.map(m)) ) } + def applyConstraints(c: Constraints): Element = this + def applyConstraintsTo(c: Constraints, idx: Int): Element = this } @@ -315,6 +388,7 @@ private[floorplan] final case class MemElement( parent: String ) extends AbstractRectPrimitive { def mapNames(m: (String) => String): Element = this.copy(name = m(name), parent = m(parent)) + def applyConstraints(c: Constraints): Element = this } // Container for MemElements @@ -335,6 +409,12 @@ private[floorplan] final case class MemElementArray( elements = elements.map(_.map(m)) ) } + def applyConstraints(c: Constraints): Element = this.copy( + width = width.and(c.width), + height = height.and(c.height), + area = area.and(c.area), + aspectRatio = aspectRatio.and(c.aspectRatio) + ) } // Container for MemElements that have been converted to Macros @@ -358,6 +438,12 @@ private[floorplan] final case class MemMacroArray( elements = elements.map(_.map(m)) ) } + def applyConstraints(c: Constraints): Element = this.copy( + width = width.and(c.width), + height = height.and(c.height), + area = area.and(c.area), + aspectRatio = aspectRatio.and(c.aspectRatio) + ) } // Reference to a macro blackbox with unknown dimensions @@ -367,6 +453,7 @@ private[floorplan] final case class AbstractMacro ( parent: String ) extends AbstractRectPrimitive { def mapNames(m: (String) => String): Element = this.copy(name = m(name), parent = m(parent)) + def applyConstraints(c: Constraints): Element = this } // Reference to a macro blackbox that has known dimensions @@ -377,6 +464,7 @@ private[floorplan] final case class SizedMacro ( height: BigDecimal ) extends SizedRectPrimitive { def mapNames(m: (String) => String): Element = this.copy(name = m(name), parent = m(parent)) + def applyConstraints(c: Constraints): Element = this } // Reference to a macro blackbox that has known dimensions and has been placed @@ -389,5 +477,6 @@ private[floorplan] final case class PlacedMacro ( height: BigDecimal ) extends PlacedRectPrimitive { def mapNames(m: (String) => String): Element = this.copy(name = m(name), parent = m(parent)) + def applyConstraints(c: Constraints): Element = this } diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/ConstraintPropagationPass.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/ConstraintPropagationPass.scala index dc90043f..2465f304 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/ConstraintPropagationPass.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/ConstraintPropagationPass.scala @@ -7,8 +7,6 @@ class ConstraintPropagationPass(val topMod: String) extends Pass { def execute(state: FloorplanState): FloorplanState = { val tree = new FloorplanTree(state, topMod) - import Constraints._ - // TODO This probably should use a SAT solver or something fancier // Top-down pass @@ -19,9 +17,17 @@ class ConstraintPropagationPass(val topMod: String) extends Pass { case e: PlacedHierarchicalTop => Constraints.sized(e.width, e.height) case e: ConstrainedWeightedGrid => - ??? // TODO + val width = e.width(e.flatIndexOf(node.record.element.name)) + val height = e.height(e.flatIndexOf(node.record.element.name)) + val area = e.area(e.flatIndexOf(node.record.element.name)) + val aspectRatio = e.aspectRatio(e.flatIndexOf(node.record.element.name)) + Constraints(width, height, area, aspectRatio) case e: ConstrainedElasticGrid => - ??? // TODO + val width = e.width(e.flatIndexOf(node.record.element.name)) + val height = e.height(e.flatIndexOf(node.record.element.name)) + val area = e.area(e.flatIndexOf(node.record.element.name)) + val aspectRatio = e.aspectRatio(e.flatIndexOf(node.record.element.name)) + Constraints(width, height, area, aspectRatio) case e: SizedGrid => val (x, y) = e.indexOf(node.record.element.name).get Constraints.sized(e.widths(x), e.heights(y)) @@ -30,7 +36,7 @@ class ConstraintPropagationPass(val topMod: String) extends Pass { case _ => ??? // Many types can never be parents and shouldn't get here }).getOrElse(Constraints()) // Only modify child - (None, Some(node.record.copy(element = applyConstraints(node.record.element, constraints)))) + (None, Some(node.record.copy(element = node.record.element.applyConstraints(constraints)))) } // Bottom-up pass @@ -54,9 +60,15 @@ class ConstraintPropagationPass(val topMod: String) extends Pass { case e: PlacedHierarchicalTop => Constraints.sized(e.width, e.height) case e: ConstrainedWeightedGrid => - ??? // TODO + val width = e.width.reduce(_+_) + val height = e.height.reduce(_+_) + val area = e.area.reduce(_+_) + Constraints(width, height, area, Unconstrained()) case e: ConstrainedElasticGrid => - ??? // TODO + val width = e.width.reduce(_+_) + val height = e.height.reduce(_+_) + val area = e.area.reduce(_+_) + Constraints(width, height, area, Unconstrained()) case e: SizedGrid => Constraints.sized(e.widths.sum, e.heights.sum) case e: MemMacroArray => @@ -68,13 +80,16 @@ class ConstraintPropagationPass(val topMod: String) extends Pass { case _ => ??? } + val newElementOpt = node.parent.map(_.record.element match { + case e: Grid => e.applyConstraintsTo(constraints, e.flatIndexOf(node.record.element.name)) + case e => e.applyConstraints(constraints) + }) // Only modify parent - (Some(node.record.copy(element = applyConstraintsUp(node.record.element, constraints, idx))), None) + (newElementOpt.map(e => node.record.copy(element = e)), None) } // TODO propagate constraints to siblings - //tree.toState - state + tree.toState } } From 71e9606dfc5da55a4133884f0df53225766b8c1f Mon Sep 17 00:00:00 2001 From: John Wright Date: Sun, 18 Jul 2021 23:08:45 -0700 Subject: [PATCH 46/73] Some constraint prop works --- .../compiler/ConstraintPropagationPass.scala | 31 +++++++++++++++++-- .../floorplan/compiler/FloorplanTree.scala | 15 ++++----- 2 files changed, 37 insertions(+), 9 deletions(-) diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/ConstraintPropagationPass.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/ConstraintPropagationPass.scala index 2465f304..c82cfe13 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/ConstraintPropagationPass.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/ConstraintPropagationPass.scala @@ -9,6 +9,31 @@ class ConstraintPropagationPass(val topMod: String) extends Pass { // TODO This probably should use a SAT solver or something fancier + // Propagate macro sizes to MemMacroArrays + val nodes = tree.allNodes.filter({ case (k,v) => + v.record.element.isInstanceOf[MemMacroArray] + }).values + nodes.foreach { node => + val element = node.record.element.asInstanceOf[MemMacroArray] + node.replace(node.record.copy( + element = element.applyConstraints(Constraints( + width = Unconstrained(), + height = Unconstrained(), + area = GreaterThanOrEqualTo(element.elements.map(e => + tree.getRecord(e.get).element match { + case e2: AbstractMacro => throw new Exception("Macros need to be sized") + case e2: SizedMacro => e2.width*e2.height + case e2: PlacedMacro => e2.width*e2.height + case _ => ??? + } + ).reduce(_+_)), + aspectRatio = Unconstrained() + )) + )) + println("Just replaced a node") + } + + // Top-down pass tree.traverseMapPre { node => val constraints: Constraints = node.parent.map(_.record.element match { @@ -32,7 +57,7 @@ class ConstraintPropagationPass(val topMod: String) extends Pass { val (x, y) = e.indexOf(node.record.element.name).get Constraints.sized(e.widths(x), e.heights(y)) case e: MemMacroArray => - Constraints() // These *should* be hard macros at this point, so no need to constrain them + Constraints() // These *should* be hard macros at this point, so no need to constrain them. Maybe aspect ratio? case _ => ??? // Many types can never be parents and shouldn't get here }).getOrElse(Constraints()) // Only modify child @@ -82,7 +107,9 @@ class ConstraintPropagationPass(val topMod: String) extends Pass { val newElementOpt = node.parent.map(_.record.element match { case e: Grid => e.applyConstraintsTo(constraints, e.flatIndexOf(node.record.element.name)) - case e => e.applyConstraints(constraints) + case e: ConstrainedHierarchicalTop => e.applyConstraints(constraints) + case e: PlacedHierarchicalTop => e.applyConstraints(constraints) + case e => e }) // Only modify parent (newElementOpt.map(e => node.record.copy(element = e)), None) diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanTree.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanTree.scala index fa5c00a8..86af2552 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanTree.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanTree.scala @@ -26,11 +26,13 @@ class FloorplanTree(val state: FloorplanState, val topMod: String) { def replace(r: FloorplanElementRecord) { _record = r } } - val allRecords: Map[String, FloorplanElementRecord] = state.records.map({ x => (x.element.name -> x) }).toMap - - def getRecord(s: String): FloorplanElementRecord = allRecords(s) + def getRecord(s: String): FloorplanElementRecord = getNode(s).record def getNode(s: String): Node = allNodes(s) + // These are only used by the constructor + private val allRecords: Map[String, FloorplanElementRecord] = state.records.map({ x => (x.element.name -> x) }).toMap + private def _getRecord(s: String): FloorplanElementRecord = allRecords(s) + val topRecords = state.records.flatMap({ r => r.element match { case e: Top => Seq(r) case _ => Seq() @@ -38,13 +40,12 @@ class FloorplanTree(val state: FloorplanState, val topMod: String) { assert(topRecords.length == 1, "Must be exactly one Top record") val topRecord = topRecords(0) - private def dfs(parent: Option[Node], r: FloorplanElementRecord): Node = { r.element match { case e: Top => assert(!parent.isDefined, "Cannot have multiple tops") val n = new Node(None, r) - dfs(Some(n), getRecord(e.topGroup)) + dfs(Some(n), _getRecord(e.topGroup)) n case e: Primitive => assert(parent.isDefined, "Must have parent") @@ -52,7 +53,7 @@ class FloorplanTree(val state: FloorplanState, val topMod: String) { case e: Group => assert(parent.isDefined, "Must have parent") val n = parent.get.addChildRecord(r) - e.elements.foreach(_.foreach(x => dfs(Some(n), getRecord(x)))) + e.elements.foreach(_.foreach(x => dfs(Some(n), _getRecord(x)))) n case _ => ??? } @@ -84,7 +85,7 @@ class FloorplanTree(val state: FloorplanState, val topMod: String) { } def toState: FloorplanState = { - val records = allRecords.values.toSeq + val records = allNodes.values.map(_.record).toSeq val level = records.map(_.element.level).max FloorplanState(records, level) } From 128c298b8b7b64ab8351dcd748f944df54a88071 Mon Sep 17 00:00:00 2001 From: John Wright Date: Sun, 18 Jul 2021 23:09:34 -0700 Subject: [PATCH 47/73] Remove accidental println --- .../barstools/floorplan/compiler/ConstraintPropagationPass.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/ConstraintPropagationPass.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/ConstraintPropagationPass.scala index c82cfe13..ae73fd92 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/ConstraintPropagationPass.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/ConstraintPropagationPass.scala @@ -30,7 +30,6 @@ class ConstraintPropagationPass(val topMod: String) extends Pass { aspectRatio = Unconstrained() )) )) - println("Just replaced a node") } From e292849dc00a69f49b520f67ffc3f453979fe94e Mon Sep 17 00:00:00 2001 From: John Wright Date: Wed, 21 Jul 2021 11:32:48 -0700 Subject: [PATCH 48/73] API tweak --- .../barstools/floorplan/Constraints.scala | 2 +- .../barstools/floorplan/chisel/API.scala | 59 +++++++++++++++---- 2 files changed, 49 insertions(+), 12 deletions(-) diff --git a/floorplan/src/main/scala/barstools/floorplan/Constraints.scala b/floorplan/src/main/scala/barstools/floorplan/Constraints.scala index 00f5e356..0bda2d2b 100644 --- a/floorplan/src/main/scala/barstools/floorplan/Constraints.scala +++ b/floorplan/src/main/scala/barstools/floorplan/Constraints.scala @@ -10,7 +10,7 @@ sealed trait Constraint { final class Unconstrained extends Constraint { def and(that: Constraint) = that - def +(that: Constraint) = that + def +(that: Constraint) = that // ??? } object Unconstrained { diff --git a/floorplan/src/main/scala/barstools/floorplan/chisel/API.scala b/floorplan/src/main/scala/barstools/floorplan/chisel/API.scala index ec4dcc01..20e229c7 100644 --- a/floorplan/src/main/scala/barstools/floorplan/chisel/API.scala +++ b/floorplan/src/main/scala/barstools/floorplan/chisel/API.scala @@ -68,7 +68,7 @@ final class ChiselFloorplanContext private[chisel] (val root: Target, val topEle def createElasticArray(dim: Int): ChiselElasticArray = createElasticArray(dim, Direction.Horizontal, None) def createElasticArray(elts: Seq[ChiselElement], dir: Direction = Direction.Horizontal, name: Option[String] = None): ChiselElasticArray = { val ary = createElasticArray(elts.length, dir, name) - elts.zipWithIndex.foreach { case (e, i) => ary.placeAt(e, i) } + elts.zipWithIndex.foreach { case (e, i) => ary.placeAt(i, e) } ary } @@ -101,14 +101,25 @@ final class ChiselFloorplanContext private[chisel] (val root: Target, val topEle object Floorplan { + + def apply[T <: RawModule](module: T): ChiselFloorplanContext = apply( + module = module, + width = Unconstrained(), + height = Unconstrained(), + area = Unconstrained(), + aspectRatio = Unconstrained(), + hardBoundary = true, + margins = Margins.empty + ) + def apply[T <: RawModule](module: T, - width: Constraint = Unconstrained(), - height: Constraint = Unconstrained(), - area: Constraint = Unconstrained(), - aspectRatio: Constraint = Unconstrained(), - hardBoundary: Boolean = true, - margins: Margins = Margins.empty - ) = { + width: Constraint, + height: Constraint, + area: Constraint, + aspectRatio: Constraint, + hardBoundary: Boolean, + margins: Margins + ): ChiselFloorplanContext = { val root: Target = module.toAbsoluteTarget val modName = root match { case r: InstanceTarget => r.ofModule @@ -121,6 +132,32 @@ object Floorplan { new ChiselFloorplanContext(root, elt) } + def apply[T <: RawModule](module: T, + width: Double, + height: Double + ): ChiselFloorplanContext = apply( + module = module, + width = EqualTo(BigDecimal(width)), + height = EqualTo(BigDecimal(height)), + area = Unconstrained(), + aspectRatio = Unconstrained(), + hardBoundary = true, + margins = Margins.empty + ) + + def apply[T <: RawModule](module: T, + width: String, + height: String + ): ChiselFloorplanContext = apply( + module = module, + width = EqualTo(BigDecimal(width)), + height = EqualTo(BigDecimal(height)), + area = Unconstrained(), + aspectRatio = Unconstrained(), + hardBoundary = true, + margins = Margins.empty + ) + } private[chisel] object FloorplanDatabase { @@ -270,7 +307,7 @@ sealed abstract class ChiselGroupElement(root: Target, name: String, val context generateGroupElement(elements.map(_.map(_.name))) } - private[chisel] def _placeAt[T <: ChiselElement](e: T, idx: Int): T = { + private[chisel] def _placeAt[T <: ChiselElement](idx: Int, e: T): T = { assert(!isCommitted, "Cannot add elements after committing") // This is only supported in scala 2.13 //elements.padToInPlace(idx+1, None) @@ -299,7 +336,7 @@ abstract class ChiselGridElement(root: Target, name: String, context: ChiselFloo protected def initialSize = xDim * yDim protected def toIdx(x: Int, y: Int): Int = xDim*y + x - def placeAt[T <: ChiselElement](e: T, x: Int, y: Int): T = _placeAt(e, toIdx(x, y)) + def placeAt[T <: ChiselElement](x: Int, y: Int, e: T): T = _placeAt(toIdx(x, y), e) } @@ -397,5 +434,5 @@ class ChiselElasticArray private[chisel] ( dim: Int, val dir: Direction ) extends ChiselElasticGrid(root, name, context, dir.ifH(dim,1), dir.ifV(dim,1)) { - def placeAt[T <: ChiselElement](e: T, i: Int): T = placeAt(e, dir.ifH(i,0), dir.ifV(0,i)) + def placeAt[T <: ChiselElement](i: Int, e: T): T = placeAt(dir.ifH(i,0), dir.ifV(0,i), e) } From 9d96f3eed58eeed6b49d3938458cf38baf6be623 Mon Sep 17 00:00:00 2001 From: John Wright Date: Wed, 21 Jul 2021 11:56:46 -0700 Subject: [PATCH 49/73] weights -> [xy]Weights --- floorplan/src/main/scala/barstools/floorplan/IR.scala | 9 ++++++--- .../floorplan/compiler/ReplaceAbstractGroupsPass.scala | 3 ++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/floorplan/src/main/scala/barstools/floorplan/IR.scala b/floorplan/src/main/scala/barstools/floorplan/IR.scala index f7fcb8d4..e9002f59 100644 --- a/floorplan/src/main/scala/barstools/floorplan/IR.scala +++ b/floorplan/src/main/scala/barstools/floorplan/IR.scala @@ -243,7 +243,8 @@ private[floorplan] final case class WeightedGrid( xDim: Int, yDim: Int, elements: Seq[Option[String]], - weights: Seq[BigDecimal], + xWeights: Seq[BigDecimal], + yWeights: Seq[BigDecimal], packed: Boolean ) extends Grid { def level = 3 @@ -261,7 +262,8 @@ private[floorplan] final case class WeightedGrid( xDim = xDim, yDim = yDim, elements = elements, - weights = weights, + xWeights = xWeights, + yWeights = yWeights, packed = packed, width = (Seq.fill(xDim*yDim) { Unconstrained() }).updated(idx, c.width), height = (Seq.fill(xDim*yDim) { Unconstrained() }).updated(idx, c.height), @@ -276,7 +278,8 @@ private[floorplan] final case class ConstrainedWeightedGrid( xDim: Int, yDim: Int, elements: Seq[Option[String]], - weights: Seq[BigDecimal], + xWeights: Seq[BigDecimal], + yWeights: Seq[BigDecimal], packed: Boolean, width: Seq[Constraint], height: Seq[Constraint], diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceAbstractGroupsPass.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceAbstractGroupsPass.scala index 97d5dea9..8c405def 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceAbstractGroupsPass.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceAbstractGroupsPass.scala @@ -14,7 +14,8 @@ class ReplaceAbstractGroupsPass extends Pass { xDim = e.xDim, yDim = e.yDim, elements = e.elements, - weights = e.weights, + xWeights = e.xWeights, + yWeights = e.yWeights, packed = e.packed, width = Seq.fill(e.xDim*e.yDim) { Unconstrained() }, height = Seq.fill(e.xDim*e.yDim) { Unconstrained() }, From 4b2a787b578cf71483716ad6cca6a83fbb824bb6 Mon Sep 17 00:00:00 2001 From: John Wright Date: Sat, 24 Jul 2021 21:32:02 -0700 Subject: [PATCH 50/73] Sideband->OutOfBand --- .../compiler/FloorplanCompiler.scala | 4 ++-- ...tation.scala => OutOfBandAnnotation.scala} | 22 +++++++++---------- ...ss.scala => OutOfBandAnnotationPass.scala} | 10 ++++----- .../OutOfBandAnnotationSerialization.scala | 19 ++++++++++++++++ .../barstools/floorplan/compiler/Pass.scala | 4 ++-- .../SidebandAnnotationSerialization.scala | 19 ---------------- 6 files changed, 39 insertions(+), 39 deletions(-) rename floorplan/src/main/scala/barstools/floorplan/compiler/{SidebandAnnotation.scala => OutOfBandAnnotation.scala} (54%) rename floorplan/src/main/scala/barstools/floorplan/compiler/{SidebandAnnotationPass.scala => OutOfBandAnnotationPass.scala} (88%) create mode 100644 floorplan/src/main/scala/barstools/floorplan/compiler/OutOfBandAnnotationSerialization.scala delete mode 100644 floorplan/src/main/scala/barstools/floorplan/compiler/SidebandAnnotationSerialization.scala diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanCompiler.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanCompiler.scala index 58dc692d..74e7893c 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanCompiler.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanCompiler.scala @@ -40,9 +40,9 @@ object FloorplanCompiler extends App { action((x, c) => c.copy(memInstMapFiles = c.memInstMapFiles :+ x)). text("file containing the memory instance map") - opt[String]('b', "sideband-anno-file"). + opt[String]('b', "oob-anno-file"). required(). - valueName(""). + valueName(""). action((x, c) => c.copy(sbAnnoFiles = c.sbAnnoFiles :+ x)). text("output file name") diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/SidebandAnnotation.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/OutOfBandAnnotation.scala similarity index 54% rename from floorplan/src/main/scala/barstools/floorplan/compiler/SidebandAnnotation.scala rename to floorplan/src/main/scala/barstools/floorplan/compiler/OutOfBandAnnotation.scala index 068eabc8..6e8e6eb2 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/SidebandAnnotation.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/OutOfBandAnnotation.scala @@ -4,18 +4,18 @@ package barstools.floorplan.compiler import barstools.floorplan._ import scala.collection.{Seq, Map} -case class SidebandAnnotation( +case class OutOfBandAnnotation( ofModule: String, width: Option[BigDecimal] = None, height: Option[BigDecimal] = None, area: Option[BigDecimal] = None ) { - def ++(that: SidebandAnnotation): SidebandAnnotation = { + def ++(that: OutOfBandAnnotation): OutOfBandAnnotation = { assert(this.ofModule == that.ofModule) assert(!this.width.isDefined || !that.width.isDefined) assert(!this.height.isDefined || !that.height.isDefined) assert(!this.area.isDefined || !that.area.isDefined) - SidebandAnnotation( + OutOfBandAnnotation( this.ofModule, this.width.orElse(that.width), this.height.orElse(that.width), @@ -28,17 +28,17 @@ case class SidebandAnnotation( def areaConstraint = area.map(x => EqualTo(x)).getOrElse(Unconstrained()) } -object SidebandAnnotationMap { - def fromFiles(files: Seq[String]): Map[String, SidebandAnnotation] = fromSeq(SidebandAnnotationSeq.fromFiles(files)) - def fromFile(file: String): Map[String, SidebandAnnotation] = fromSeq(SidebandAnnotationSeq.fromFile(file)) - def fromSeq(seq: Seq[SidebandAnnotation]): Map[String, SidebandAnnotation] = seq.groupBy(_.ofModule).mapValues(_.reduce(_ ++ _)) +object OutOfBandAnnotationMap { + def fromFiles(files: Seq[String]): Map[String, OutOfBandAnnotation] = fromSeq(OutOfBandAnnotationSeq.fromFiles(files)) + def fromFile(file: String): Map[String, OutOfBandAnnotation] = fromSeq(OutOfBandAnnotationSeq.fromFile(file)) + def fromSeq(seq: Seq[OutOfBandAnnotation]): Map[String, OutOfBandAnnotation] = seq.groupBy(_.ofModule).mapValues(_.reduce(_ ++ _)) } -object SidebandAnnotationSeq { - def fromFiles(files: Seq[String]): Seq[SidebandAnnotation] = files.flatMap(x => fromFile(x)) - def fromFile(file: String): Seq[SidebandAnnotation] = { +object OutOfBandAnnotationSeq { + def fromFiles(files: Seq[String]): Seq[OutOfBandAnnotation] = files.flatMap(x => fromFile(x)) + def fromFile(file: String): Seq[OutOfBandAnnotation] = { val source = scala.io.Source.fromFile(file) - val annos = SidebandAnnotationSerialization.deserialize(source.getLines.mkString("\n")) + val annos = OutOfBandAnnotationSerialization.deserialize(source.getLines.mkString("\n")) source.close() annos } diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/SidebandAnnotationPass.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/OutOfBandAnnotationPass.scala similarity index 88% rename from floorplan/src/main/scala/barstools/floorplan/compiler/SidebandAnnotationPass.scala rename to floorplan/src/main/scala/barstools/floorplan/compiler/OutOfBandAnnotationPass.scala index c7668d81..1ec19698 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/SidebandAnnotationPass.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/OutOfBandAnnotationPass.scala @@ -4,7 +4,7 @@ package barstools.floorplan.compiler import barstools.floorplan._ import scala.collection.Map -class SidebandAnnotationPass(val sbMap: Map[String, SidebandAnnotation]) extends Pass { +class OutOfBandAnnotationPass(val sbMap: Map[String, OutOfBandAnnotation]) extends Pass { def execute(state: FloorplanState): FloorplanState = { val newRecords = state.records.map { record => val ofModule = record.ofModule.getOrElse("") @@ -48,7 +48,7 @@ class SidebandAnnotationPass(val sbMap: Map[String, SidebandAnnotation]) extends }).getOrElse(e) case e: AbstractMacro => sbMap.get(ofModule).map({sb => - assert(sb.width.isDefined && sb.height.isDefined, "Macro sideband annotations must include width and height") + assert(sb.width.isDefined && sb.height.isDefined, "Macro out-of-band annotations must include width and height") SizedMacro( name = e.name, parent = e.parent, @@ -58,7 +58,7 @@ class SidebandAnnotationPass(val sbMap: Map[String, SidebandAnnotation]) extends }).getOrElse(e) case e: SizedMacro => sbMap.get(ofModule).map({sb => - assert(sb.width.isDefined && sb.height.isDefined, "Macro sideband annotations must include width and height") + assert(sb.width.isDefined && sb.height.isDefined, "Macro out-of-band annotations must include width and height") e.copy( width = sb.width.get, height = sb.height.get @@ -66,7 +66,7 @@ class SidebandAnnotationPass(val sbMap: Map[String, SidebandAnnotation]) extends }).getOrElse(e) case e: PlacedMacro => sbMap.get(ofModule).map({sb => - assert(sb.width.isDefined && sb.height.isDefined, "Macro sideband annotations must include width and height") + assert(sb.width.isDefined && sb.height.isDefined, "Macro out-of-band annotations must include width and height") e.copy( width = sb.width.get, height = sb.height.get @@ -78,7 +78,7 @@ class SidebandAnnotationPass(val sbMap: Map[String, SidebandAnnotation]) extends } newRecords.map(_.element).collectFirst { case e: AbstractMacro => - throw new Exception("Unannoated macros still exist after SidebandAnnotationPass") + throw new Exception("Unannotated macros still exist after OutOfBandAnnotationPass") } state.copy(records = newRecords) } diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/OutOfBandAnnotationSerialization.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/OutOfBandAnnotationSerialization.scala new file mode 100644 index 00000000..145033b6 --- /dev/null +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/OutOfBandAnnotationSerialization.scala @@ -0,0 +1,19 @@ +// See LICENSE for license details +package barstools.floorplan.compiler + +import org.json4s._ +import org.json4s.native.Serialization.{read, write, writePretty} + +object OutOfBandAnnotationSerialization { + + val formats = DefaultFormats.skippingEmptyValues + + def serialize(seq: Seq[OutOfBandAnnotation]): String = writePretty(seq)(formats) + + def deserialize(str: String): Seq[OutOfBandAnnotation] = { + implicit val formats = OutOfBandAnnotationSerialization.formats + read[Seq[OutOfBandAnnotation]](str) + } + +} + diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/Pass.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/Pass.scala index 77a8df84..3ae2b5c5 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/Pass.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/Pass.scala @@ -7,10 +7,10 @@ object Pass { def all(opts: FloorplanOptions): Seq[Pass] = { val instMap = MemInstMap.fromFiles(opts.memInstMapFiles) - val sbAnnos = SidebandAnnotationMap.fromFiles(opts.sbAnnoFiles) + val sbAnnos = OutOfBandAnnotationMap.fromFiles(opts.sbAnnoFiles) Seq( new TransformMemsPass(instMap), - new SidebandAnnotationPass(sbAnnos), + new OutOfBandAnnotationPass(sbAnnos), new ReplaceAbstractGroupsPass, new ReplaceHierarchicalPass(opts.topMod), new ConstraintPropagationPass(opts.topMod), diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/SidebandAnnotationSerialization.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/SidebandAnnotationSerialization.scala deleted file mode 100644 index 428eaa98..00000000 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/SidebandAnnotationSerialization.scala +++ /dev/null @@ -1,19 +0,0 @@ -// See LICENSE for license details -package barstools.floorplan.compiler - -import org.json4s._ -import org.json4s.native.Serialization.{read, write, writePretty} - -object SidebandAnnotationSerialization { - - val formats = DefaultFormats.skippingEmptyValues - - def serialize(seq: Seq[SidebandAnnotation]): String = writePretty(seq)(formats) - - def deserialize(str: String): Seq[SidebandAnnotation] = { - implicit val formats = SidebandAnnotationSerialization.formats - read[Seq[SidebandAnnotation]](str) - } - -} - From d968ab3812f50dddb1e09d91a275f91d8bc85946 Mon Sep 17 00:00:00 2001 From: John Wright Date: Sun, 25 Jul 2021 11:39:56 -0700 Subject: [PATCH 51/73] root->scope --- .../barstools/floorplan/FloorplanState.scala | 4 +- .../barstools/floorplan/chisel/API.scala | 120 +++++++++--------- .../compiler/ReplaceHierarchicalPass.scala | 50 ++++---- .../compiler/TransformMemsPass.scala | 4 +- .../barstools/floorplan/firrtl/Passes.scala | 20 +-- 5 files changed, 99 insertions(+), 99 deletions(-) diff --git a/floorplan/src/main/scala/barstools/floorplan/FloorplanState.scala b/floorplan/src/main/scala/barstools/floorplan/FloorplanState.scala index bd80cdcb..01d0b2a6 100644 --- a/floorplan/src/main/scala/barstools/floorplan/FloorplanState.scala +++ b/floorplan/src/main/scala/barstools/floorplan/FloorplanState.scala @@ -4,8 +4,8 @@ package barstools.floorplan import barstools.floorplan.hammer.HammerIR import java.io.{File, FileWriter} -final case class FloorplanElementRecord(root: String, inst: Option[String], ofModule: Option[String], element: Element) { - def fullPath = root + inst.map(x => "/" + x).getOrElse("") +final case class FloorplanElementRecord(scope: String, inst: Option[String], ofModule: Option[String], element: Element) { + def fullPath = scope + inst.map(x => "/" + x).getOrElse("") } final case class FloorplanState(records: Seq[FloorplanElementRecord], level: Int) diff --git a/floorplan/src/main/scala/barstools/floorplan/chisel/API.scala b/floorplan/src/main/scala/barstools/floorplan/chisel/API.scala index 20e229c7..486091ba 100644 --- a/floorplan/src/main/scala/barstools/floorplan/chisel/API.scala +++ b/floorplan/src/main/scala/barstools/floorplan/chisel/API.scala @@ -12,14 +12,14 @@ import scala.math.{BigInt, BigDecimal} final case class ChiselFloorplanException(message: String) extends Exception(message: String) -final class ChiselFloorplanContext private[chisel] (val root: Target, val topElement: ChiselHierarchicalTop) { +final class ChiselFloorplanContext private[chisel] (val scope: Target, val topElement: ChiselHierarchicalTop) { private[chisel] val elementBuf = new ArrayBuffer[ChiselElement]() elementBuf.append(topElement) private def addElement[T <: ChiselElement](e: T): T = { - FloorplanDatabase.register(root, e) + FloorplanDatabase.register(scope, e) elementBuf.append(e) e } @@ -34,8 +34,8 @@ final class ChiselFloorplanContext private[chisel] (val root: Target, val topEle hardBoundary: Boolean = true ): ChiselLogicRect = { val inst = module.toAbsoluteTarget.asInstanceOf[InstanceTarget] - val name = FloorplanDatabase.getUnusedName(root, inst) - val elt = new ChiselLogicRect(root, name, inst, width, height, area, aspectRatio, hardBoundary) + val name = FloorplanDatabase.getUnusedName(scope, inst) + val elt = new ChiselLogicRect(scope, name, inst, width, height, area, aspectRatio, hardBoundary) addElement(elt) } @@ -46,20 +46,20 @@ final class ChiselFloorplanContext private[chisel] (val root: Target, val topEle area: Constraint = Unconstrained(), aspectRatio: Constraint = Unconstrained() ): ChiselSpacerRect = { - val nameStr = FloorplanDatabase.getUnusedName(root, name) - val elt = new ChiselSpacerRect(root, nameStr, width, height, area, aspectRatio) + val nameStr = FloorplanDatabase.getUnusedName(scope, name) + val elt = new ChiselSpacerRect(scope, nameStr, width, height, area, aspectRatio) addElement(elt) } def createElasticGrid(xDim: Int, yDim: Int, name: Option[String] = None): ChiselElasticGrid = { - val nameStr = FloorplanDatabase.getUnusedName(root, name) - val elt = new ChiselElasticGrid(root, nameStr, this, xDim, yDim) + val nameStr = FloorplanDatabase.getUnusedName(scope, name) + val elt = new ChiselElasticGrid(scope, nameStr, this, xDim, yDim) addElement(elt) } def createElasticArray(dim: Int, dir: Direction, name: Option[String]): ChiselElasticArray = { - val nameStr = FloorplanDatabase.getUnusedName(root, name) - val elt = new ChiselElasticArray(root, nameStr, this, dim, dir) + val nameStr = FloorplanDatabase.getUnusedName(scope, name) + val elt = new ChiselElasticArray(scope, nameStr, this, dim, dir) addElement(elt) } @@ -73,22 +73,22 @@ final class ChiselFloorplanContext private[chisel] (val root: Target, val topEle } def createMemArray(name: Option[String] = None): ChiselMemArray = { - val nameStr = FloorplanDatabase.getUnusedName(root, name) - val elt = new ChiselMemArray(root, nameStr, this) + val nameStr = FloorplanDatabase.getUnusedName(scope, name) + val elt = new ChiselMemArray(scope, nameStr, this) addElement(elt) } def addHier[T <: RawModule](module: T): ChiselHierarchicalBarrier = { val inst = module.toAbsoluteTarget.asInstanceOf[InstanceTarget] - val name = FloorplanDatabase.getUnusedName(root, inst) - val elt = new ChiselHierarchicalBarrier(root, name, inst) + val name = FloorplanDatabase.getUnusedName(scope, inst) + val elt = new ChiselHierarchicalBarrier(scope, name, inst) addElement(elt) } private[chisel] def addMem[T <: Data](mem: MemBase[T]): ChiselMem = { val ref = mem.toAbsoluteTarget - val name = FloorplanDatabase.getUnusedName(root, ref) - val elt = new ChiselMem(root, name, ref) + val name = FloorplanDatabase.getUnusedName(scope, ref) + val elt = new ChiselMem(scope, name, ref) addElement(elt) } @@ -120,16 +120,16 @@ object Floorplan { hardBoundary: Boolean, margins: Margins ): ChiselFloorplanContext = { - val root: Target = module.toAbsoluteTarget - val modName = root match { + val scope: Target = module.toAbsoluteTarget + val modName = scope match { case r: InstanceTarget => r.ofModule case r: ModuleTarget => r.module case _ => ??? } - val name = FloorplanDatabase.getUnusedName(root, modName) - val elt = new ChiselHierarchicalTop(root, name, width, height, area, aspectRatio, margins, hardBoundary) - FloorplanDatabase.register(root, elt) - new ChiselFloorplanContext(root, elt) + val name = FloorplanDatabase.getUnusedName(scope, modName) + val elt = new ChiselHierarchicalTop(scope, name, width, height, area, aspectRatio, margins, hardBoundary) + FloorplanDatabase.register(scope, elt) + new ChiselFloorplanContext(scope, elt) } def apply[T <: RawModule](module: T, @@ -165,39 +165,39 @@ private[chisel] object FloorplanDatabase { private val nameMap = new HashMap[Target, Set[String]]() private val elements = new HashSet[ChiselElement]() - private def getSet(root: Target) = nameMap.getOrElseUpdate(root, new HashSet[String]) + private def getSet(scope: Target) = nameMap.getOrElseUpdate(scope, new HashSet[String]) // TODO I'm not sure this is necessary anymore - def register(root: Target, element: ChiselElement): Unit = { + def register(scope: Target, element: ChiselElement): Unit = { val name = element.name - val set = getSet(root) + val set = getSet(scope) if (set.contains(name)) { - throw new ChiselFloorplanException(s"Duplicate floorplan element registration ${name} for Target "+root.toString+"!") + throw new ChiselFloorplanException(s"Duplicate floorplan element registration ${name} for Target "+scope.toString+"!") } elements.add(element) set.add(name) } - def getUnusedName(root: Target): String = getUnusedName(root, None) + def getUnusedName(scope: Target): String = getUnusedName(scope, None) - def getUnusedName(root: Target, suggestion: Option[String]): String = getUnusedName(root, suggestion.getOrElse("unnamed")) + def getUnusedName(scope: Target, suggestion: Option[String]): String = getUnusedName(scope, suggestion.getOrElse("unnamed")) - def getUnusedName(root: Target, suggestion: String): String = { - val set = getSet(root) + def getUnusedName(scope: Target, suggestion: String): String = { + val set = getSet(scope) var id = 0 // This is slow and bad, but hopefully rare while (set.contains(suggestion + s"_${id}")) { id = id + 1 } suggestion + s"_${id}" } - def getUnusedName(root: Target, inst: Target): String = { + def getUnusedName(scope: Target, inst: Target): String = { val instName = inst match { case t: InstanceTarget => t.instance case t: ModuleTarget => t.module case t: ReferenceTarget => t.ref case _ => ??? } - getUnusedName(root, instName) + getUnusedName(scope, instName) } } @@ -261,7 +261,7 @@ object Direction { case object Horizontal extends Direction } -sealed abstract class ChiselElement(val root: Target, val name: String) { +sealed abstract class ChiselElement(val scope: Target, val name: String) { protected def generateElement(): Element private[chisel] def getFloorplanAnnotations(): Seq[FloorplanAnnotation] def getAnnotations(): Seq[Annotation] = getFloorplanAnnotations() @@ -281,20 +281,20 @@ sealed trait CanBeParent { def name: String } -sealed abstract class ChiselNoReferenceElement(root: Target, name: String) extends ChiselElement(root, name) { - private[chisel] def getFloorplanAnnotations() = Seq(NoReferenceFloorplanAnnotation(root, generateElement())) +sealed abstract class ChiselNoReferenceElement(scope: Target, name: String) extends ChiselElement(scope, name) { + private[chisel] def getFloorplanAnnotations() = Seq(NoReferenceFloorplanAnnotation(scope, generateElement())) } -sealed abstract class ChiselInstanceElement(root: Target, name: String, val instance: Target) extends ChiselElement(root, name) { - private[chisel] def getFloorplanAnnotations() = Seq(InstanceFloorplanAnnotation(Seq(Seq(root), Seq(instance)), generateElement())) +sealed abstract class ChiselInstanceElement(scope: Target, name: String, val instance: Target) extends ChiselElement(scope, name) { + private[chisel] def getFloorplanAnnotations() = Seq(InstanceFloorplanAnnotation(Seq(Seq(scope), Seq(instance)), generateElement())) } -sealed abstract class ChiselMemElement(root: Target, name: String, val reference: ReferenceTarget) extends ChiselElement(root, name) { - private[chisel] def getFloorplanAnnotations() = Seq(MemFloorplanAnnotation(Seq(Seq(root), Seq(reference)), generateElement())) +sealed abstract class ChiselMemElement(scope: Target, name: String, val reference: ReferenceTarget) extends ChiselElement(scope, name) { + private[chisel] def getFloorplanAnnotations() = Seq(MemFloorplanAnnotation(Seq(Seq(scope), Seq(reference)), generateElement())) } -sealed abstract class ChiselGroupElement(root: Target, name: String, val context: ChiselFloorplanContext) - extends ChiselElement(root, name) with CanBeParent { +sealed abstract class ChiselGroupElement(scope: Target, name: String, val context: ChiselFloorplanContext) + extends ChiselElement(scope, name) with CanBeParent { protected def initialSize: Int protected val elements = Seq.fill(initialSize)(Option.empty[ChiselElement]).toBuffer @@ -318,10 +318,10 @@ sealed abstract class ChiselGroupElement(root: Target, name: String, val context e } - private[chisel] def getFloorplanAnnotations() = Seq(NoReferenceFloorplanAnnotation(root, generateElement())) + private[chisel] def getFloorplanAnnotations() = Seq(NoReferenceFloorplanAnnotation(scope, generateElement())) } -abstract class ChiselArrayElement(root: Target, name: String, context: ChiselFloorplanContext) extends ChiselGroupElement(root, name, context) { +abstract class ChiselArrayElement(scope: Target, name: String, context: ChiselFloorplanContext) extends ChiselGroupElement(scope, name, context) { private[chisel] def addElement(e: ChiselElement) { assert(!isCommitted, "Cannot add elements after committing") @@ -331,7 +331,7 @@ abstract class ChiselArrayElement(root: Target, name: String, context: ChiselFlo } -abstract class ChiselGridElement(root: Target, name: String, context: ChiselFloorplanContext, val xDim: Int, val yDim: Int) extends ChiselGroupElement(root, name, context) { +abstract class ChiselGridElement(scope: Target, name: String, context: ChiselFloorplanContext, val xDim: Int, val yDim: Int) extends ChiselGroupElement(scope, name, context) { protected def initialSize = xDim * yDim protected def toIdx(x: Int, y: Int): Int = xDim*y + x @@ -341,7 +341,7 @@ abstract class ChiselGridElement(root: Target, name: String, context: ChiselFloo } final class ChiselHierarchicalTop private[chisel] ( - root: Target, + scope: Target, name: String, val width: Constraint, val height: Constraint, @@ -349,7 +349,7 @@ final class ChiselHierarchicalTop private[chisel] ( val aspectRatio: Constraint, val margins: Margins, val hardBoundary: Boolean -) extends ChiselNoReferenceElement(root, name) with CanBeParent { +) extends ChiselNoReferenceElement(scope, name) with CanBeParent { private var topGroup = Option.empty[ChiselGroupElement] private def topGroupName: String = { @@ -367,15 +367,15 @@ final class ChiselHierarchicalTop private[chisel] ( } final class ChiselHierarchicalBarrier private[chisel] ( - root: Target, + scope: Target, name: String, instance: InstanceTarget -) extends ChiselInstanceElement(root, name, instance) { +) extends ChiselInstanceElement(scope, name, instance) { protected def generateElement(): Element = HierarchicalBarrier(name, parentName) } final class ChiselLogicRect private[chisel] ( - root: Target, + scope: Target, name: String, instance: InstanceTarget, val width: Constraint, @@ -383,34 +383,34 @@ final class ChiselLogicRect private[chisel] ( val area: Constraint, val aspectRatio: Constraint, val hardBoundary: Boolean -) extends ChiselInstanceElement(root, name, instance) { +) extends ChiselInstanceElement(scope, name, instance) { protected def generateElement(): Element = ConstrainedLogicRect(name, parentName, width, height, area, aspectRatio, hardBoundary) } final class ChiselSpacerRect private[chisel] ( - root: Target, + scope: Target, name: String, val width: Constraint = Unconstrained(), val height: Constraint = Unconstrained(), val area: Constraint = Unconstrained(), val aspectRatio: Constraint = Unconstrained() -) extends ChiselNoReferenceElement(root, name) { +) extends ChiselNoReferenceElement(scope, name) { protected def generateElement(): Element = ConstrainedSpacerRect(name, parentName, width, height, area, aspectRatio) } final class ChiselMem private[chisel] ( - root: Target, + scope: Target, name: String, reference: ReferenceTarget -) extends ChiselMemElement(root, name, reference) { +) extends ChiselMemElement(scope, name, reference) { protected def generateElement(): Element = MemElement(name, parentName) } final class ChiselMemArray private[chisel] ( - root: Target, + scope: Target, name: String, context: ChiselFloorplanContext -) extends ChiselArrayElement(root, name, context) { +) extends ChiselArrayElement(scope, name, context) { protected def initialSize = 0 protected def generateGroupElement(names: Seq[Option[String]]): Group = MemElementArray(name, parentName, names) @@ -418,21 +418,21 @@ final class ChiselMemArray private[chisel] ( } class ChiselElasticGrid private[chisel] ( - root: Target, + scope: Target, name: String, context: ChiselFloorplanContext, xDim: Int, yDim: Int -) extends ChiselGridElement(root, name, context, xDim, yDim) { +) extends ChiselGridElement(scope, name, context, xDim, yDim) { final protected def generateGroupElement(names: Seq[Option[String]]): Group = ElasticGrid(name, parentName, xDim, yDim, names) } class ChiselElasticArray private[chisel] ( - root: Target, + scope: Target, name: String, context: ChiselFloorplanContext, dim: Int, val dir: Direction -) extends ChiselElasticGrid(root, name, context, dir.ifH(dim,1), dir.ifV(dim,1)) { +) extends ChiselElasticGrid(scope, name, context, dir.ifH(dim,1), dir.ifV(dim,1)) { def placeAt[T <: ChiselElement](i: Int, e: T): T = placeAt(dir.ifH(i,0), dir.ifV(0,i), e) } diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceHierarchicalPass.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceHierarchicalPass.scala index 94dc2d9e..f005e22b 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceHierarchicalPass.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceHierarchicalPass.scala @@ -17,27 +17,27 @@ class ReplaceHierarchicalPass(val topMod: String) extends Pass { case _ => None })).toMap - val root = topMaps(topMod).root - val rootNameSet = HashSet(state.records.filter(_.root == root).map(_.element.name):_*) + val scope = topMaps(topMod).scope + val scopeNameSet = HashSet(state.records.filter(_.scope == scope).map(_.element.name):_*) - val nonRootPaths = topMaps.filterKeys(_ != topMod).values.map(_.root) + val nonScopePaths = topMaps.filterKeys(_ != topMod).values.map(_.scope) def getUniqueName(suggestion: String): String = { var i = 0 var tmp = suggestion + s"_${i}" - while (rootNameSet.contains(tmp)) { + while (scopeNameSet.contains(tmp)) { i = i + 1 tmp = suggestion + s"_${i}" } - rootNameSet += tmp + scopeNameSet += tmp tmp } val renameMap = new HashMap[(String, String), String]() - def rename(oldRoot: String, oldName: String): String = { - if (oldRoot == rootNameSet) { return oldName } - val tup = (oldRoot, oldName) + def rename(oldScope: String, oldName: String): String = { + if (oldScope == scopeNameSet) { return oldName } + val tup = (oldScope, oldName) if (renameMap.contains(tup)) { return renameMap(tup) } val newName = getUniqueName(oldName) renameMap += (tup -> newName) @@ -45,8 +45,8 @@ class ReplaceHierarchicalPass(val topMod: String) extends Pass { } def getRelPath(fullPath: Option[String]): Option[String] = fullPath.map { p => - assert(p.startsWith(root), "Full path must be in root scope") - p.substring(root.length) + assert(p.startsWith(scope), "Full path must be in scope scope") + p.substring(scope.length) } val newRecords = state.records.flatMap({r => r.element match { @@ -58,21 +58,21 @@ class ReplaceHierarchicalPass(val topMod: String) extends Pass { // We replace with two elements (one to replace each name); they'll get optimized away later // Parent is first (replaces e), child is after (replaces t) Seq(ConstrainedElasticGrid( - name = rename(r.root, e.name), - parent = rename(r.root, e.parent), + name = rename(r.scope, e.name), + parent = rename(r.scope, e.parent), xDim = 1, yDim = 1, - elements = Seq(Some(rename(tr.root, t.name))), + elements = Seq(Some(rename(tr.scope, t.name))), width = Seq(t.width), height = Seq(t.height), area = Seq(t.area), aspectRatio = Seq(t.aspectRatio) ), ConstrainedElasticGrid( - name = rename(tr.root, t.name), - parent = rename(r.root, e.name), + name = rename(tr.scope, t.name), + parent = rename(r.scope, e.name), xDim = 1, yDim = 1, - elements = Seq(Some(rename(tr.root, t.topGroup))), + elements = Seq(Some(rename(tr.scope, t.topGroup))), width = Seq(t.width), height = Seq(t.height), area = Seq(t.area), @@ -80,25 +80,25 @@ class ReplaceHierarchicalPass(val topMod: String) extends Pass { )) case t: PlacedHierarchicalTop => Seq(SizedGrid( - name = rename(r.root, e.name), - parent = rename(r.root, e.parent), + name = rename(r.scope, e.name), + parent = rename(r.scope, e.parent), xDim = 1, yDim = 1, - elements = Seq(Some(rename(tr.root, t.name))), + elements = Seq(Some(rename(tr.scope, t.name))), widths = Seq(t.width), heights = Seq(t.height) ), SizedGrid( - name = rename(tr.root, t.name), - parent = rename(r.root, e.name), + name = rename(tr.scope, t.name), + parent = rename(r.scope, e.name), xDim = 1, yDim = 1, - elements = Seq(Some(rename(tr.root, t.topGroup))), + elements = Seq(Some(rename(tr.scope, t.topGroup))), widths = Seq(t.width), heights = Seq(t.height) )) case _ => ??? }).map(newE => FloorplanElementRecord( - root = root, + scope = scope, inst = getRelPath(r.inst.map(_ => r.fullPath)), ofModule = r.ofModule, element = newE @@ -106,10 +106,10 @@ class ReplaceHierarchicalPass(val topMod: String) extends Pass { case e: ConstrainedHierarchicalTop if r.ofModule != Some(topMod) => Seq() case e: PlacedHierarchicalTop if r.ofModule != Some(topMod) => Seq() case e => Seq(r.copy( - root = root, + scope = scope, inst = getRelPath(r.inst.map(_ => r.fullPath)), ofModule = r.ofModule, - element = r.element.mapNames(x => rename(r.root, x)) + element = r.element.mapNames(x => rename(r.scope, x)) )) }}) state.copy(records = newRecords, level = 2) // TODO recalculate level diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/TransformMemsPass.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/TransformMemsPass.scala index cc48ea7c..2eb9f975 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/TransformMemsPass.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/TransformMemsPass.scala @@ -37,7 +37,7 @@ class TransformMemsPass(instMap: Map[OfModule, Seq[(Instance, OfModule)]]) exten val element = AbstractMacro(getUniqueName(e.name + "_" + ofMod.value), e.parent) renameMap.update(e.name, renameMap.getOrElse(e.name, Set()) ++ Set(element.name)) FloorplanElementRecord( - root = record.root, + scope = record.scope, inst = record.inst.map(_ + "/" + inst.value), ofModule = Some(ofMod.value), element = element @@ -58,7 +58,7 @@ class TransformMemsPass(instMap: Map[OfModule, Seq[(Instance, OfModule)]]) exten aspectRatio = e.aspectRatio ) FloorplanElementRecord( - root = record.root, + scope = record.scope, inst = record.inst, // should always be None ofModule = None, element = element diff --git a/floorplan/src/main/scala/barstools/floorplan/firrtl/Passes.scala b/floorplan/src/main/scala/barstools/floorplan/firrtl/Passes.scala index 8edc310b..aad52949 100644 --- a/floorplan/src/main/scala/barstools/floorplan/firrtl/Passes.scala +++ b/floorplan/src/main/scala/barstools/floorplan/firrtl/Passes.scala @@ -31,11 +31,11 @@ class GenerateFloorplanIRPass extends Transform with RegisteredTransform with De (Seq(it.module) ++ it.asPath.toList.map(_._1.value)).mkString("/") } getOrElse state.circuit.main - def getRelativePath(root: Option[InstanceTarget], inst: Option[IsComponent]): String = { - val rootPath = root.map(_.asPath).getOrElse(Seq()) + def getRelativePath(scope: Option[InstanceTarget], inst: Option[IsComponent]): String = { + val scopePath = scope.map(_.asPath).getOrElse(Seq()) val instPath = inst.map(_.asPath).getOrElse(Seq()) - assert(instPath.take(rootPath.length) == rootPath, s"InstanceTarget ${instPath} must be inside ${rootPath}") - val pathStr = instPath.drop(rootPath.length).toList.map(_._1.value).mkString("/") + assert(instPath.take(scopePath.length) == scopePath, s"InstanceTarget ${instPath} must be inside ${scopePath}") + val pathStr = instPath.drop(scopePath.length).toList.map(_._1.value).mkString("/") inst.map(_ match { case x: InstanceTarget => pathStr case x: ReferenceTarget => pathStr + "." + x.ref @@ -48,16 +48,16 @@ class GenerateFloorplanIRPass extends Transform with RegisteredTransform with De val list = state.annotations.collect({ case x: NoReferenceFloorplanAnnotation => - val (rootTarget, ofModule) = x.target match { + val (scopeTarget, ofModule) = x.target match { case y: InstanceTarget => (Some(y), Some(y.ofModule)) case y: ModuleTarget => assert(y.module == state.circuit.main, "ModuleTarget is only supported for the top module") (Option.empty[InstanceTarget], Some(y.module)) case _ => ??? } - newRecord(getInstancePath(rootTarget), None, ofModule, x) + newRecord(getInstancePath(scopeTarget), None, ofModule, x) case x: InstanceFloorplanAnnotation if x.targets.flatten.length == 2 => - val rootTarget = x.targets(0)(0) match { + val scopeTarget = x.targets(0)(0) match { case y: InstanceTarget => Some(y) case y: ModuleTarget => assert(y.module == state.circuit.main, "ModuleTarget is only supported for the top module") @@ -71,9 +71,9 @@ class GenerateFloorplanIRPass extends Transform with RegisteredTransform with De (Option.empty[InstanceTarget], Some(y.module)) case _ => ??? } - newRecord(getInstancePath(rootTarget), Some(getRelativePath(rootTarget, instTarget)), ofModule, x) + newRecord(getInstancePath(scopeTarget), Some(getRelativePath(scopeTarget, instTarget)), ofModule, x) case x: MemFloorplanAnnotation if x.targets.flatten.length == 2 => - val rootTarget = x.targets(0)(0) match { + val scopeTarget = x.targets(0)(0) match { case y: InstanceTarget => Some(y) case y: ModuleTarget => assert(y.module == state.circuit.main, "ModuleTarget is only supported for the top module") @@ -95,7 +95,7 @@ class GenerateFloorplanIRPass extends Transform with RegisteredTransform with De instance=ext, ofModule=ext, path=refTarget.path :+ (Instance(refTarget.ref), OfModule(mem))) - newRecord(getInstancePath(rootTarget), Some(getRelativePath(rootTarget, Some(newTarget))), Some(ext), x) + newRecord(getInstancePath(scopeTarget), Some(getRelativePath(scopeTarget, Some(newTarget))), Some(ext), x) }) val filename = state.annotations.collectFirst({ From 36c97e48054866739442d000d62c0568695b16b4 Mon Sep 17 00:00:00 2001 From: John Wright Date: Sun, 25 Jul 2021 15:32:54 -0700 Subject: [PATCH 52/73] Remove unnecessary Abstract*Grid --- .../barstools/floorplan/Constraints.scala | 4 ++ .../main/scala/barstools/floorplan/IR.scala | 66 ------------------- .../barstools/floorplan/chisel/API.scala | 11 +++- .../barstools/floorplan/compiler/Pass.scala | 1 - .../compiler/ReplaceAbstractGroupsPass.scala | 43 ------------ 5 files changed, 14 insertions(+), 111 deletions(-) delete mode 100644 floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceAbstractGroupsPass.scala diff --git a/floorplan/src/main/scala/barstools/floorplan/Constraints.scala b/floorplan/src/main/scala/barstools/floorplan/Constraints.scala index 0bda2d2b..305af278 100644 --- a/floorplan/src/main/scala/barstools/floorplan/Constraints.scala +++ b/floorplan/src/main/scala/barstools/floorplan/Constraints.scala @@ -18,6 +18,10 @@ object Unconstrained { def apply() = u } +object UnconstrainedSeq { + def apply(x: Int) = Seq.fill(x) { Unconstrained() } +} + // TODO add a reason? final class Impossible extends Constraint { def and(that: Constraint) = this diff --git a/floorplan/src/main/scala/barstools/floorplan/IR.scala b/floorplan/src/main/scala/barstools/floorplan/IR.scala index e9002f59..0955545f 100644 --- a/floorplan/src/main/scala/barstools/floorplan/IR.scala +++ b/floorplan/src/main/scala/barstools/floorplan/IR.scala @@ -236,42 +236,6 @@ sealed abstract class Grid extends Group { def applyConstraintsTo(c: Constraints, x: Int, y: Int): Element = applyConstraintsTo(c, toIdx(x, y)) } -// TODO eventually rename this to AbstractWeightedGrid -private[floorplan] final case class WeightedGrid( - name: String, - parent: String, - xDim: Int, - yDim: Int, - elements: Seq[Option[String]], - xWeights: Seq[BigDecimal], - yWeights: Seq[BigDecimal], - packed: Boolean -) extends Grid { - def level = 3 - def mapNames(m: (String) => String): Element = { - this.copy( - name = m(name), - parent = m(parent), - elements = elements.map(_.map(m)) - ) - } - def applyConstraints(c: Constraints): Element = this // TODO this is NOT correct - def applyConstraintsTo(c: Constraints, idx: Int): Element = ConstrainedWeightedGrid( - name = name, - parent = parent, - xDim = xDim, - yDim = yDim, - elements = elements, - xWeights = xWeights, - yWeights = yWeights, - packed = packed, - width = (Seq.fill(xDim*yDim) { Unconstrained() }).updated(idx, c.width), - height = (Seq.fill(xDim*yDim) { Unconstrained() }).updated(idx, c.height), - area = (Seq.fill(xDim*yDim) { Unconstrained() }).updated(idx, c.area), - aspectRatio = (Seq.fill(xDim*yDim) { Unconstrained() }).updated(idx, c.aspectRatio) - ) -} - private[floorplan] final case class ConstrainedWeightedGrid( name: String, parent: String, @@ -303,36 +267,6 @@ private[floorplan] final case class ConstrainedWeightedGrid( ) } -// TODO eventually rename this to AbstractElasticGrid -private[floorplan] final case class ElasticGrid( - name: String, - parent: String, - xDim: Int, - yDim: Int, - elements: Seq[Option[String]] -) extends Grid { - def level = 3 - def mapNames(m: (String) => String): Element = { - this.copy( - name = m(name), - parent = m(parent), - elements = elements.map(_.map(m)) - ) - } - def applyConstraints(c: Constraints): Element = this // TODO this is NOT correct - def applyConstraintsTo(c: Constraints, idx: Int): Element = ConstrainedElasticGrid( - name = name, - parent = parent, - xDim = xDim, - yDim = yDim, - elements = elements, - width = (Seq.fill(xDim*yDim) { Unconstrained() }).updated(idx, c.width), - height = (Seq.fill(xDim*yDim) { Unconstrained() }).updated(idx, c.height), - area = (Seq.fill(xDim*yDim) { Unconstrained() }).updated(idx, c.area), - aspectRatio = (Seq.fill(xDim*yDim) { Unconstrained() }).updated(idx, c.aspectRatio) - ) -} - private[floorplan] final case class ConstrainedElasticGrid( name: String, parent: String, diff --git a/floorplan/src/main/scala/barstools/floorplan/chisel/API.scala b/floorplan/src/main/scala/barstools/floorplan/chisel/API.scala index 486091ba..03bf3baf 100644 --- a/floorplan/src/main/scala/barstools/floorplan/chisel/API.scala +++ b/floorplan/src/main/scala/barstools/floorplan/chisel/API.scala @@ -424,7 +424,16 @@ class ChiselElasticGrid private[chisel] ( xDim: Int, yDim: Int ) extends ChiselGridElement(scope, name, context, xDim, yDim) { - final protected def generateGroupElement(names: Seq[Option[String]]): Group = ElasticGrid(name, parentName, xDim, yDim, names) + final protected def generateGroupElement(names: Seq[Option[String]]): Group = ConstrainedElasticGrid( + name, + parentName, + xDim, + yDim, + names, + UnconstrainedSeq(xDim*yDim), + UnconstrainedSeq(xDim*yDim), + UnconstrainedSeq(xDim*yDim), + UnconstrainedSeq(xDim*yDim)) } class ChiselElasticArray private[chisel] ( diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/Pass.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/Pass.scala index 3ae2b5c5..a289e4fb 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/Pass.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/Pass.scala @@ -11,7 +11,6 @@ object Pass { Seq( new TransformMemsPass(instMap), new OutOfBandAnnotationPass(sbAnnos), - new ReplaceAbstractGroupsPass, new ReplaceHierarchicalPass(opts.topMod), new ConstraintPropagationPass(opts.topMod), new ReplaceMemMacroArrayPass, diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceAbstractGroupsPass.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceAbstractGroupsPass.scala deleted file mode 100644 index 8c405def..00000000 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceAbstractGroupsPass.scala +++ /dev/null @@ -1,43 +0,0 @@ -// See LICENSE for license details -package barstools.floorplan.compiler - -import barstools.floorplan._ - -class ReplaceAbstractGroupsPass extends Pass { - def execute(state: FloorplanState): FloorplanState = { - val newRecords = state.records.map { record => - val newElement = record.element match { - case e: WeightedGrid => - Some(ConstrainedWeightedGrid( - name = e.name, - parent = e.parent, - xDim = e.xDim, - yDim = e.yDim, - elements = e.elements, - xWeights = e.xWeights, - yWeights = e.yWeights, - packed = e.packed, - width = Seq.fill(e.xDim*e.yDim) { Unconstrained() }, - height = Seq.fill(e.xDim*e.yDim) { Unconstrained() }, - area = Seq.fill(e.xDim*e.yDim) { Unconstrained() }, - aspectRatio = Seq.fill(e.xDim*e.yDim) { Unconstrained() } - )) - case e: ElasticGrid => - Some(ConstrainedElasticGrid( - name = e.name, - parent = e.parent, - xDim = e.xDim, - yDim = e.yDim, - elements = e.elements, - width = Seq.fill(e.xDim*e.yDim) { Unconstrained() }, - height = Seq.fill(e.xDim*e.yDim) { Unconstrained() }, - area = Seq.fill(e.xDim*e.yDim) { Unconstrained() }, - aspectRatio = Seq.fill(e.xDim*e.yDim) { Unconstrained() } - )) - case _ => None - } - newElement.map(e => record.copy(element = e)).getOrElse(record) - } - state.copy(records = newRecords) - } -} From 273997164b5ab994ec9062233fa52adde79b60b7 Mon Sep 17 00:00:00 2001 From: John Wright Date: Sun, 25 Jul 2021 16:01:58 -0700 Subject: [PATCH 53/73] FloorplanElementRecord->FloorplanRecord --- .../barstools/floorplan/FloorplanState.scala | 8 +++---- .../floorplan/compiler/FloorplanTree.scala | 24 +++++++++---------- .../compiler/ReplaceHierarchicalPass.scala | 2 +- .../compiler/TransformMemsPass.scala | 4 ++-- .../barstools/floorplan/firrtl/Passes.scala | 4 ++-- 5 files changed, 21 insertions(+), 21 deletions(-) diff --git a/floorplan/src/main/scala/barstools/floorplan/FloorplanState.scala b/floorplan/src/main/scala/barstools/floorplan/FloorplanState.scala index 01d0b2a6..57d639b1 100644 --- a/floorplan/src/main/scala/barstools/floorplan/FloorplanState.scala +++ b/floorplan/src/main/scala/barstools/floorplan/FloorplanState.scala @@ -4,18 +4,18 @@ package barstools.floorplan import barstools.floorplan.hammer.HammerIR import java.io.{File, FileWriter} -final case class FloorplanElementRecord(scope: String, inst: Option[String], ofModule: Option[String], element: Element) { +final case class FloorplanRecord(scope: String, inst: Option[String], ofModule: Option[String], element: Element) { def fullPath = scope + inst.map(x => "/" + x).getOrElse("") } -final case class FloorplanState(records: Seq[FloorplanElementRecord], level: Int) +final case class FloorplanState(records: Seq[FloorplanRecord], level: Int) object FloorplanState { - def fromSeq(seq: Seq[FloorplanElementRecord]): FloorplanState = FloorplanState(seq, (Seq(IRLevel.max) ++ seq.map(_.element.level)).max) + def fromSeq(seq: Seq[FloorplanRecord]): FloorplanState = FloorplanState(seq, (Seq(IRLevel.max) ++ seq.map(_.element.level)).max) def serialize(state: FloorplanState): String = FloorplanSerialization.serializeState(state) - def serialize(seq: Seq[FloorplanElementRecord]): String = serialize(fromSeq(seq)) + def serialize(seq: Seq[FloorplanRecord]): String = serialize(fromSeq(seq)) def deserialize(str: String): FloorplanState = FloorplanSerialization.deserializeState(str) diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanTree.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanTree.scala index 86af2552..dcf74968 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanTree.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanTree.scala @@ -8,7 +8,7 @@ class FloorplanTree(val state: FloorplanState, val topMod: String) { val allNodes = new HashMap[String, Node]() - class Node(val parent: Option[Node], initialRecord: FloorplanElementRecord) { + class Node(val parent: Option[Node], initialRecord: FloorplanRecord) { val children = new ArrayBuffer[Node]() // TODO this might be dangerous @@ -16,22 +16,22 @@ class FloorplanTree(val state: FloorplanState, val topMod: String) { def record = _record - def addChildRecord(cRecord: FloorplanElementRecord): Node = { + def addChildRecord(cRecord: FloorplanRecord): Node = { val n = new Node(Some(this), cRecord) children += n allNodes += (cRecord.element.name -> n) n } - def replace(r: FloorplanElementRecord) { _record = r } + def replace(r: FloorplanRecord) { _record = r } } - def getRecord(s: String): FloorplanElementRecord = getNode(s).record + def getRecord(s: String): FloorplanRecord = getNode(s).record def getNode(s: String): Node = allNodes(s) // These are only used by the constructor - private val allRecords: Map[String, FloorplanElementRecord] = state.records.map({ x => (x.element.name -> x) }).toMap - private def _getRecord(s: String): FloorplanElementRecord = allRecords(s) + private val allRecords: Map[String, FloorplanRecord] = state.records.map({ x => (x.element.name -> x) }).toMap + private def _getRecord(s: String): FloorplanRecord = allRecords(s) val topRecords = state.records.flatMap({ r => r.element match { case e: Top => Seq(r) @@ -40,7 +40,7 @@ class FloorplanTree(val state: FloorplanState, val topMod: String) { assert(topRecords.length == 1, "Must be exactly one Top record") val topRecord = topRecords(0) - private def dfs(parent: Option[Node], r: FloorplanElementRecord): Node = { + private def dfs(parent: Option[Node], r: FloorplanRecord): Node = { r.element match { case e: Top => assert(!parent.isDefined, "Cannot have multiple tops") @@ -62,22 +62,22 @@ class FloorplanTree(val state: FloorplanState, val topMod: String) { val topNode = dfs(None, topRecord) // Traverse using DFS, passing the node to a function which expects an - // (Option[FloorplanElementRecord], Option[FloorplanElementRecord]) return + // (Option[FloorplanRecord], Option[FloorplanRecord]) return // (None, None) = do no modify // (None, Some(record)) = modify node // (Some(record), None) = modify parent // (Some(r1), Some(r2)) = modify both - def traverseMapPre(f: (Node => (Option[FloorplanElementRecord], Option[FloorplanElementRecord]))) { traverseMapPreHelper(topNode, f) } - def traverseMapPost(f: (Node => (Option[FloorplanElementRecord], Option[FloorplanElementRecord]))) { traverseMapPostHelper(topNode, f) } + def traverseMapPre(f: (Node => (Option[FloorplanRecord], Option[FloorplanRecord]))) { traverseMapPreHelper(topNode, f) } + def traverseMapPost(f: (Node => (Option[FloorplanRecord], Option[FloorplanRecord]))) { traverseMapPostHelper(topNode, f) } - private def traverseMapPreHelper(n: Node, f: (Node => (Option[FloorplanElementRecord], Option[FloorplanElementRecord]))) { + private def traverseMapPreHelper(n: Node, f: (Node => (Option[FloorplanRecord], Option[FloorplanRecord]))) { val (parent, child) = f(n) parent.foreach { r => n.parent.foreach(_.replace(r)) } child.foreach { r => n.replace(r) } n.children.foreach { c => traverseMapPreHelper(c, f) } } - private def traverseMapPostHelper(n: Node, f: (Node => (Option[FloorplanElementRecord], Option[FloorplanElementRecord]))) { + private def traverseMapPostHelper(n: Node, f: (Node => (Option[FloorplanRecord], Option[FloorplanRecord]))) { n.children.foreach { c => traverseMapPostHelper(c, f) } val (parent, child) = f(n) parent.foreach { r => n.parent.foreach(_.replace(r)) } diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceHierarchicalPass.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceHierarchicalPass.scala index f005e22b..797ace86 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceHierarchicalPass.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceHierarchicalPass.scala @@ -97,7 +97,7 @@ class ReplaceHierarchicalPass(val topMod: String) extends Pass { heights = Seq(t.height) )) case _ => ??? - }).map(newE => FloorplanElementRecord( + }).map(newE => FloorplanRecord( scope = scope, inst = getRelPath(r.inst.map(_ => r.fullPath)), ofModule = r.ofModule, diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/TransformMemsPass.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/TransformMemsPass.scala index 2eb9f975..ca439e47 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/TransformMemsPass.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/TransformMemsPass.scala @@ -36,7 +36,7 @@ class TransformMemsPass(instMap: Map[OfModule, Seq[(Instance, OfModule)]]) exten nameSet.remove(e.name) val element = AbstractMacro(getUniqueName(e.name + "_" + ofMod.value), e.parent) renameMap.update(e.name, renameMap.getOrElse(e.name, Set()) ++ Set(element.name)) - FloorplanElementRecord( + FloorplanRecord( scope = record.scope, inst = record.inst.map(_ + "/" + inst.value), ofModule = Some(ofMod.value), @@ -57,7 +57,7 @@ class TransformMemsPass(instMap: Map[OfModule, Seq[(Instance, OfModule)]]) exten area = e.area, aspectRatio = e.aspectRatio ) - FloorplanElementRecord( + FloorplanRecord( scope = record.scope, inst = record.inst, // should always be None ofModule = None, diff --git a/floorplan/src/main/scala/barstools/floorplan/firrtl/Passes.scala b/floorplan/src/main/scala/barstools/floorplan/firrtl/Passes.scala index aad52949..afe0db56 100644 --- a/floorplan/src/main/scala/barstools/floorplan/firrtl/Passes.scala +++ b/floorplan/src/main/scala/barstools/floorplan/firrtl/Passes.scala @@ -1,7 +1,7 @@ // See LICENSE for license details package barstools.floorplan.firrtl -import barstools.floorplan.{FloorplanSerialization, FloorplanElementRecord, FloorplanState} +import barstools.floorplan.{FloorplanSerialization, FloorplanRecord, FloorplanState} import firrtl.{CircuitState, Transform, DependencyAPIMigration, VerilogEmitter, AnnotationSeq} import firrtl.options.{Dependency, RegisteredTransform, ShellOption} import firrtl.analyses.{IRLookup} @@ -44,7 +44,7 @@ class GenerateFloorplanIRPass extends Transform with RegisteredTransform with De } def newRecord(path: String, ref: Option[String], ofModule: Option[String], anno: FloorplanAnnotation) = - FloorplanElementRecord(path, ref, ofModule, FloorplanSerialization.deserialize(anno.fpir)) + FloorplanRecord(path, ref, ofModule, FloorplanSerialization.deserialize(anno.fpir)) val list = state.annotations.collect({ case x: NoReferenceFloorplanAnnotation => From 684d35abe0b1b8f5938f23fe467bd7bdaebbfa33 Mon Sep 17 00:00:00 2001 From: John Wright Date: Sun, 25 Jul 2021 19:53:06 -0700 Subject: [PATCH 54/73] ConstrainedHierarchicalTop should be level 2 --- floorplan/src/main/scala/barstools/floorplan/IR.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/floorplan/src/main/scala/barstools/floorplan/IR.scala b/floorplan/src/main/scala/barstools/floorplan/IR.scala index 0955545f..f105e0ec 100644 --- a/floorplan/src/main/scala/barstools/floorplan/IR.scala +++ b/floorplan/src/main/scala/barstools/floorplan/IR.scala @@ -185,7 +185,7 @@ private[floorplan] final case class ConstrainedHierarchicalTop( margins: Margins, hardBoundary: Boolean ) extends Top with ConstrainedRectLike { - final def level = 3 + final def level = 2 def mapNames(m: (String) => String): Element = this.copy(name = m(name), topGroup = m(topGroup)) def applyConstraints(c: Constraints): Element = this.copy( width = width.and(c.width), From 662ff3bce7878cec65833788c021dfd656748a6a Mon Sep 17 00:00:00 2001 From: John Wright Date: Sun, 8 Aug 2021 17:48:27 -0700 Subject: [PATCH 55/73] Change PlacedHierarchicalTop topGroup to elements --- .../main/scala/barstools/floorplan/IR.scala | 11 +++++------ .../compiler/ConstraintPropagationPass.scala | 2 -- .../floorplan/compiler/FloorplanTree.scala | 8 +++++++- .../compiler/ReplaceHierarchicalPass.scala | 19 +------------------ 4 files changed, 13 insertions(+), 27 deletions(-) diff --git a/floorplan/src/main/scala/barstools/floorplan/IR.scala b/floorplan/src/main/scala/barstools/floorplan/IR.scala index f105e0ec..26042580 100644 --- a/floorplan/src/main/scala/barstools/floorplan/IR.scala +++ b/floorplan/src/main/scala/barstools/floorplan/IR.scala @@ -36,10 +36,7 @@ sealed abstract class Group extends ElementWithParent { def flatIndexOf(s: String): Int = elements.indexOf(Some(s)) } -sealed abstract class Top extends Element { - def topGroup: String - def flatIndexOf(s: String): Int = if (topGroup == s) 0 else -1 -} +sealed abstract class Top extends Element ////////////////////////////////////////////// Hierarchical barrier @@ -187,6 +184,7 @@ private[floorplan] final case class ConstrainedHierarchicalTop( ) extends Top with ConstrainedRectLike { final def level = 2 def mapNames(m: (String) => String): Element = this.copy(name = m(name), topGroup = m(topGroup)) + def flatIndexOf(s: String): Int = if (topGroup == s) 0 else -1 def applyConstraints(c: Constraints): Element = this.copy( width = width.and(c.width), height = height.and(c.height), @@ -197,7 +195,7 @@ private[floorplan] final case class ConstrainedHierarchicalTop( private[floorplan] final case class PlacedHierarchicalTop( name: String, - topGroup: String, + elements: Seq[String], width: BigDecimal, height: BigDecimal, margins: Margins, @@ -206,7 +204,8 @@ private[floorplan] final case class PlacedHierarchicalTop( final def x = BigDecimal(0) final def y = BigDecimal(0) final def level = 0 - def mapNames(m: (String) => String): Element = this.copy(name = m(name), topGroup = m(topGroup)) + def mapNames(m: (String) => String): Element = this.copy(name = m(name), elements.map(m)) + def flatIndexOf(s: String): Int = elements.indexOf(s) def applyConstraints(c: Constraints): Element = this } diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/ConstraintPropagationPass.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/ConstraintPropagationPass.scala index ae73fd92..856a01aa 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/ConstraintPropagationPass.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/ConstraintPropagationPass.scala @@ -7,8 +7,6 @@ class ConstraintPropagationPass(val topMod: String) extends Pass { def execute(state: FloorplanState): FloorplanState = { val tree = new FloorplanTree(state, topMod) - // TODO This probably should use a SAT solver or something fancier - // Propagate macro sizes to MemMacroArrays val nodes = tree.allNodes.filter({ case (k,v) => v.record.element.isInstanceOf[MemMacroArray] diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanTree.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanTree.scala index dcf74968..4c115404 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanTree.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanTree.scala @@ -45,7 +45,13 @@ class FloorplanTree(val state: FloorplanState, val topMod: String) { case e: Top => assert(!parent.isDefined, "Cannot have multiple tops") val n = new Node(None, r) - dfs(Some(n), _getRecord(e.topGroup)) + // There's probably a better way to handle these + e match { + case e: ConstrainedHierarchicalTop => + dfs(Some(n), _getRecord(e.topGroup)) + case e: PlacedHierarchicalTop => + e.elements.foreach(x => dfs(Some(n), _getRecord(x))) + } n case e: Primitive => assert(parent.isDefined, "Must have parent") diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceHierarchicalPass.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceHierarchicalPass.scala index 797ace86..5f993700 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceHierarchicalPass.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceHierarchicalPass.scala @@ -78,24 +78,7 @@ class ReplaceHierarchicalPass(val topMod: String) extends Pass { area = Seq(t.area), aspectRatio = Seq(t.aspectRatio) )) - case t: PlacedHierarchicalTop => - Seq(SizedGrid( - name = rename(r.scope, e.name), - parent = rename(r.scope, e.parent), - xDim = 1, - yDim = 1, - elements = Seq(Some(rename(tr.scope, t.name))), - widths = Seq(t.width), - heights = Seq(t.height) - ), SizedGrid( - name = rename(tr.scope, t.name), - parent = rename(r.scope, e.name), - xDim = 1, - yDim = 1, - elements = Seq(Some(rename(tr.scope, t.topGroup))), - widths = Seq(t.width), - heights = Seq(t.height) - )) + case t: PlacedHierarchicalTop => ??? // TODO not supported yet case _ => ??? }).map(newE => FloorplanRecord( scope = scope, From 150188b8a1e08a476e8902898c4152eb90a4da09 Mon Sep 17 00:00:00 2001 From: John Wright Date: Sun, 8 Aug 2021 18:25:31 -0700 Subject: [PATCH 56/73] Add weighted grid to chisel API --- .../main/scala/barstools/floorplan/IR.scala | 1 - .../barstools/floorplan/chisel/API.scala | 23 +++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/floorplan/src/main/scala/barstools/floorplan/IR.scala b/floorplan/src/main/scala/barstools/floorplan/IR.scala index 26042580..be287920 100644 --- a/floorplan/src/main/scala/barstools/floorplan/IR.scala +++ b/floorplan/src/main/scala/barstools/floorplan/IR.scala @@ -243,7 +243,6 @@ private[floorplan] final case class ConstrainedWeightedGrid( elements: Seq[Option[String]], xWeights: Seq[BigDecimal], yWeights: Seq[BigDecimal], - packed: Boolean, width: Seq[Constraint], height: Seq[Constraint], area: Seq[Constraint], diff --git a/floorplan/src/main/scala/barstools/floorplan/chisel/API.scala b/floorplan/src/main/scala/barstools/floorplan/chisel/API.scala index 03bf3baf..3dbf4c9f 100644 --- a/floorplan/src/main/scala/barstools/floorplan/chisel/API.scala +++ b/floorplan/src/main/scala/barstools/floorplan/chisel/API.scala @@ -445,3 +445,26 @@ class ChiselElasticArray private[chisel] ( ) extends ChiselElasticGrid(scope, name, context, dir.ifH(dim,1), dir.ifV(dim,1)) { def placeAt[T <: ChiselElement](i: Int, e: T): T = placeAt(dir.ifH(i,0), dir.ifV(0,i), e) } + +class ChiselWeightedGrid private[chisel] ( + scope: Target, + name: String, + context: ChiselFloorplanContext, + xDim: Int, + yDim: Int, + xWeights: Seq[BigDecimal], + yWeights: Seq[BigDecimal] +) extends ChiselGridElement(scope, name, context, xDim, yDim) { + final protected def generateGroupElement(names: Seq[Option[String]]): Group = ConstrainedWeightedGrid( + name, + parentName, + xDim, + yDim, + names, + xWeights, + yWeights, + UnconstrainedSeq(xDim*yDim), + UnconstrainedSeq(xDim*yDim), + UnconstrainedSeq(xDim*yDim), + UnconstrainedSeq(xDim*yDim)) +} From d29629191f486a991a24444fa34ae60498f496c8 Mon Sep 17 00:00:00 2001 From: John Wright Date: Mon, 9 Aug 2021 22:47:37 -0700 Subject: [PATCH 57/73] Clean up constraint prop --- .../barstools/floorplan/Constraints.scala | 51 +++++- .../main/scala/barstools/floorplan/IR.scala | 120 +++++++------- .../barstools/floorplan/chisel/API.scala | 16 +- .../compiler/ConstraintPropagationPass.scala | 149 ++++++++++-------- .../floorplan/compiler/FloorplanTree.scala | 16 +- .../compiler/ReplaceHierarchicalPass.scala | 16 +- 6 files changed, 217 insertions(+), 151 deletions(-) diff --git a/floorplan/src/main/scala/barstools/floorplan/Constraints.scala b/floorplan/src/main/scala/barstools/floorplan/Constraints.scala index 305af278..2be959a3 100644 --- a/floorplan/src/main/scala/barstools/floorplan/Constraints.scala +++ b/floorplan/src/main/scala/barstools/floorplan/Constraints.scala @@ -6,11 +6,15 @@ import scala.math.{BigInt, BigDecimal} sealed trait Constraint { def and(that: Constraint): Constraint def +(that: Constraint): Constraint + def *(that: BigDecimal): Constraint + def test(value: BigDecimal): Boolean } final class Unconstrained extends Constraint { def and(that: Constraint) = that def +(that: Constraint) = that // ??? + def *(that: BigDecimal) = this + def test(value: BigDecimal) = true } object Unconstrained { @@ -26,6 +30,8 @@ object UnconstrainedSeq { final class Impossible extends Constraint { def and(that: Constraint) = this def +(that: Constraint) = this + def *(that: BigDecimal) = this + def test(value: BigDecimal) = false } object Impossible { @@ -126,13 +132,21 @@ final case class Constrained( None } - val newLeq = if (this.leq.isDefined && that.leq.isDefined) { + val newLeq = if (this.leq.isDefined && that.eq.isDefined) { + Some(this.leq.get + that.eq.get) + } else if (this.eq.isDefined && that.leq.isDefined) { + Some(this.eq.get + that.leq.get) + } else if (this.leq.isDefined && that.leq.isDefined) { Some(this.leq.get + that.leq.get) } else { None } - val newGeq = if (this.geq.isDefined && that.geq.isDefined) { + val newGeq = if (this.geq.isDefined && that.eq.isDefined) { + Some(this.geq.get + that.eq.get) + } else if (this.eq.isDefined && that.geq.isDefined) { + Some(this.eq.get + that.geq.get) + } else if (this.geq.isDefined && that.geq.isDefined) { Some(this.geq.get + that.geq.get) } else { None @@ -148,6 +162,21 @@ final case class Constrained( case _ => ??? } } + + def *(that: BigDecimal): Constraint = Constrained( + eq = this.eq.map(_ * that), + geq = this.geq.map(_ * that), + leq = this.leq.map(_ * that), + mof = this.mof.map(_ * that) + ) + + def test(value: BigDecimal): Boolean = { + val eqTest = this.eq.map(_ == value).getOrElse(true) + val geqTest = this.geq.map(_ <= value).getOrElse(true) + val leqTest = this.leq.map(_ >= value).getOrElse(true) + val mofTest = this.mof.map(x => (value % x) == 0).getOrElse(true) + return eqTest && geqTest && leqTest && mofTest + } } object EqualTo { @@ -171,7 +200,22 @@ case class Constraints( height: Constraint = Unconstrained(), area: Constraint = Unconstrained(), aspectRatio: Constraint = Unconstrained() -) +) { + def test(widthValue: BigDecimal, heightValue: BigDecimal): Boolean = { + val widthTest = width.test(widthValue) + val heightTest = height.test(heightValue) + val areaTest = area.test(widthValue*heightValue) + val arTest = aspectRatio.test(heightValue/widthValue) + widthTest && heightTest && areaTest && arTest + } + + def weightXY(xWeight: BigDecimal, yWeight: BigDecimal): Constraints = Constraints( + width = this.width * xWeight, + height = this.height * yWeight, + area = this.area * (xWeight * yWeight), + aspectRatio = this.aspectRatio * (yWeight / xWeight) + ) +} object Constraints { @@ -183,6 +227,7 @@ object Constraints { Unconstrained() ) } + } sealed trait PlacementAnchor diff --git a/floorplan/src/main/scala/barstools/floorplan/IR.scala b/floorplan/src/main/scala/barstools/floorplan/IR.scala index be287920..7710b82e 100644 --- a/floorplan/src/main/scala/barstools/floorplan/IR.scala +++ b/floorplan/src/main/scala/barstools/floorplan/IR.scala @@ -10,16 +10,43 @@ import scala.math.{BigInt, BigDecimal} ////////////////////////////////////////////// Base classes -sealed abstract class Element { +sealed trait Element { def name: String def level: Int def serialize = FloorplanSerialization.serialize(this) def flatIndexOf(s: String): Int + def mapNames(m: (String) => String): Element + + def toConstraints: Constraints +} + +sealed trait Constrainable extends Element { def applyConstraints(c: Constraints): Element +} - def mapNames(m: (String) => String): Element +sealed trait HasFixedDimensions extends Constrainable { + def width: BigDecimal + def height: BigDecimal + def area: BigDecimal = width*height + + def testConstraints(c: Constraints): Boolean = { + return c.test(width, height) + } + + final def applyConstraints(c: Constraints): Element = { + if (this.testConstraints(c)) { + this + } else { + throw new Exception("Impossible constraints applied to previously sized element") + } + } +} + +sealed trait HasPlacement extends Element { + def x: BigDecimal + def y: BigDecimal } sealed abstract class ElementWithParent extends Element { @@ -43,38 +70,37 @@ sealed abstract class Top extends Element private[floorplan] final case class HierarchicalBarrier( name: String, parent: String -) extends Primitive { +) extends Primitive with AbstractRectLike { final def level = 3 def mapNames(m: (String) => String): Element = this.copy(name = m(name), parent = m(parent)) - def applyConstraints(c: Constraints): Element = this } ////////////////////////////////////////////// Rectangular things -sealed trait ConstrainedRectLike { +sealed trait AbstractRectLike extends Element { + def toConstraints: Constraints = Constraints() +} + +sealed trait ConstrainedRectLike extends Constrainable { def width: Constraint def height: Constraint def area: Constraint def aspectRatio: Constraint -} -sealed trait SizedRectLike { - def width: BigDecimal - def height: BigDecimal + def toConstraints: Constraints = Constraints(width, height, area, aspectRatio) } -sealed trait PlacedRectLike { - def x: BigDecimal - def y: BigDecimal - def width: BigDecimal - def height: BigDecimal +sealed trait SizedRectLike extends HasFixedDimensions { + def toConstraints: Constraints = Constraints.sized(width, height) } +sealed trait PlacedRectLike extends SizedRectLike with HasPlacement + object IRLevel { def max = 4 } -sealed abstract class AbstractRectPrimitive extends Primitive { +sealed abstract class AbstractRectPrimitive extends Primitive with AbstractRectLike { final def level = 4 } @@ -116,7 +142,6 @@ private[floorplan] final case class SizedSpacerRect( height: BigDecimal ) extends SizedRectPrimitive { def mapNames(m: (String) => String): Element = this.copy(name = m(name), parent = m(parent)) - def applyConstraints(c: Constraints): Element = this } // No PlacedSpacerRect exists because they're only for spacing @@ -155,7 +180,6 @@ private[floorplan] final case class SizedLogicRect( hardBoundary: Boolean ) extends SizedRectPrimitive { def mapNames(m: (String) => String): Element = this.copy(name = m(name), parent = m(parent)) - def applyConstraints(c: Constraints): Element = this } private[floorplan] final case class PlacedLogicRect( @@ -168,7 +192,6 @@ private[floorplan] final case class PlacedLogicRect( hardBoundary: Boolean ) extends PlacedRectPrimitive { def mapNames(m: (String) => String): Element = this.copy(name = m(name), parent = m(parent)) - def applyConstraints(c: Constraints): Element = this } @@ -206,7 +229,6 @@ private[floorplan] final case class PlacedHierarchicalTop( final def level = 0 def mapNames(m: (String) => String): Element = this.copy(name = m(name), elements.map(m)) def flatIndexOf(s: String): Int = elements.indexOf(s) - def applyConstraints(c: Constraints): Element = this } ////////////////////////////////////////////// Aggregate (Group) things @@ -228,11 +250,6 @@ sealed abstract class Grid extends Group { val idx = elements.indexOf(Some(s)) if (idx == -1) None else Some(fromIdx(idx)) } - - def applyConstraintsTo(c: Constraints, idx: Int): Element - def applyConstraintsTo(c: Constraints, xyo: Option[(Int, Int)]): Element = applyConstraintsTo(c, xyo.get) - def applyConstraintsTo(c: Constraints, xy: (Int, Int)): Element = applyConstraintsTo(c, xy._1, xy._2) - def applyConstraintsTo(c: Constraints, x: Int, y: Int): Element = applyConstraintsTo(c, toIdx(x, y)) } private[floorplan] final case class ConstrainedWeightedGrid( @@ -243,11 +260,11 @@ private[floorplan] final case class ConstrainedWeightedGrid( elements: Seq[Option[String]], xWeights: Seq[BigDecimal], yWeights: Seq[BigDecimal], - width: Seq[Constraint], - height: Seq[Constraint], - area: Seq[Constraint], - aspectRatio: Seq[Constraint] -) extends Grid { + width: Constraint, + height: Constraint, + area: Constraint, + aspectRatio: Constraint +) extends Grid with ConstrainedRectLike { def level = 2 def mapNames(m: (String) => String): Element = { this.copy( @@ -256,13 +273,15 @@ private[floorplan] final case class ConstrainedWeightedGrid( elements = elements.map(_.map(m)) ) } - def applyConstraints(c: Constraints): Element = this // TODO this is NOT correct - def applyConstraintsTo(c: Constraints, idx: Int): Element = this.copy( - width = width.updated(idx, width(idx).and(c.width)), - height = height.updated(idx, height(idx).and(c.height)), - area = area.updated(idx, area(idx).and(c.area)), - aspectRatio = aspectRatio.updated(idx, aspectRatio(idx).and(c.aspectRatio)) + def applyConstraints(c: Constraints): Element = this.copy( + width = width.and(c.width), + height = height.and(c.height), + area = area.and(c.area), + aspectRatio = aspectRatio.and(c.aspectRatio) ) + + def getXWeight(x: Int): BigDecimal = xWeights(x) / xWeights.sum + def getYWeight(y: Int): BigDecimal = yWeights(y) / yWeights.sum } private[floorplan] final case class ConstrainedElasticGrid( @@ -271,11 +290,11 @@ private[floorplan] final case class ConstrainedElasticGrid( xDim: Int, yDim: Int, elements: Seq[Option[String]], - width: Seq[Constraint], - height: Seq[Constraint], - area: Seq[Constraint], - aspectRatio: Seq[Constraint] -) extends Grid { + width: Constraint, + height: Constraint, + area: Constraint, + aspectRatio: Constraint +) extends Grid with ConstrainedRectLike { def level = 2 def mapNames(m: (String) => String): Element = { this.copy( @@ -284,12 +303,11 @@ private[floorplan] final case class ConstrainedElasticGrid( elements = elements.map(_.map(m)) ) } - def applyConstraints(c: Constraints): Element = this // TODO this is NOT correct - def applyConstraintsTo(c: Constraints, idx: Int): Element = this.copy( - width = width.updated(idx, width(idx).and(c.width)), - height = height.updated(idx, height(idx).and(c.height)), - area = area.updated(idx, area(idx).and(c.area)), - aspectRatio = aspectRatio.updated(idx, aspectRatio(idx).and(c.aspectRatio)) + def applyConstraints(c: Constraints): Element = this.copy( + width = width.and(c.width), + height = height.and(c.height), + area = area.and(c.area), + aspectRatio = aspectRatio.and(c.aspectRatio) ) } @@ -301,7 +319,7 @@ private[floorplan] final case class SizedGrid( elements: Seq[Option[String]], widths: Seq[BigDecimal], heights: Seq[BigDecimal] -) extends Grid { +) extends Grid with SizedRectLike { def level = 1 def mapNames(m: (String) => String): Element = { this.copy( @@ -310,8 +328,8 @@ private[floorplan] final case class SizedGrid( elements = elements.map(_.map(m)) ) } - def applyConstraints(c: Constraints): Element = this - def applyConstraintsTo(c: Constraints, idx: Int): Element = this + def width: BigDecimal = widths.sum + def height: BigDecimal = heights.sum } @@ -323,7 +341,6 @@ private[floorplan] final case class MemElement( parent: String ) extends AbstractRectPrimitive { def mapNames(m: (String) => String): Element = this.copy(name = m(name), parent = m(parent)) - def applyConstraints(c: Constraints): Element = this } // Container for MemElements @@ -388,7 +405,6 @@ private[floorplan] final case class AbstractMacro ( parent: String ) extends AbstractRectPrimitive { def mapNames(m: (String) => String): Element = this.copy(name = m(name), parent = m(parent)) - def applyConstraints(c: Constraints): Element = this } // Reference to a macro blackbox that has known dimensions @@ -399,7 +415,6 @@ private[floorplan] final case class SizedMacro ( height: BigDecimal ) extends SizedRectPrimitive { def mapNames(m: (String) => String): Element = this.copy(name = m(name), parent = m(parent)) - def applyConstraints(c: Constraints): Element = this } // Reference to a macro blackbox that has known dimensions and has been placed @@ -412,6 +427,5 @@ private[floorplan] final case class PlacedMacro ( height: BigDecimal ) extends PlacedRectPrimitive { def mapNames(m: (String) => String): Element = this.copy(name = m(name), parent = m(parent)) - def applyConstraints(c: Constraints): Element = this } diff --git a/floorplan/src/main/scala/barstools/floorplan/chisel/API.scala b/floorplan/src/main/scala/barstools/floorplan/chisel/API.scala index 3dbf4c9f..d57232b8 100644 --- a/floorplan/src/main/scala/barstools/floorplan/chisel/API.scala +++ b/floorplan/src/main/scala/barstools/floorplan/chisel/API.scala @@ -430,10 +430,10 @@ class ChiselElasticGrid private[chisel] ( xDim, yDim, names, - UnconstrainedSeq(xDim*yDim), - UnconstrainedSeq(xDim*yDim), - UnconstrainedSeq(xDim*yDim), - UnconstrainedSeq(xDim*yDim)) + Unconstrained(), + Unconstrained(), + Unconstrained(), + Unconstrained()) } class ChiselElasticArray private[chisel] ( @@ -463,8 +463,8 @@ class ChiselWeightedGrid private[chisel] ( names, xWeights, yWeights, - UnconstrainedSeq(xDim*yDim), - UnconstrainedSeq(xDim*yDim), - UnconstrainedSeq(xDim*yDim), - UnconstrainedSeq(xDim*yDim)) + Unconstrained(), + Unconstrained(), + Unconstrained(), + Unconstrained()) } diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/ConstraintPropagationPass.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/ConstraintPropagationPass.scala index 856a01aa..d4a179f3 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/ConstraintPropagationPass.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/ConstraintPropagationPass.scala @@ -7,49 +7,36 @@ class ConstraintPropagationPass(val topMod: String) extends Pass { def execute(state: FloorplanState): FloorplanState = { val tree = new FloorplanTree(state, topMod) - // Propagate macro sizes to MemMacroArrays - val nodes = tree.allNodes.filter({ case (k,v) => - v.record.element.isInstanceOf[MemMacroArray] - }).values - nodes.foreach { node => - val element = node.record.element.asInstanceOf[MemMacroArray] - node.replace(node.record.copy( - element = element.applyConstraints(Constraints( - width = Unconstrained(), - height = Unconstrained(), - area = GreaterThanOrEqualTo(element.elements.map(e => - tree.getRecord(e.get).element match { - case e2: AbstractMacro => throw new Exception("Macros need to be sized") - case e2: SizedMacro => e2.width*e2.height - case e2: PlacedMacro => e2.width*e2.height - case _ => ??? - } - ).reduce(_+_)), - aspectRatio = Unconstrained() - )) - )) - } - - // Top-down pass tree.traverseMapPre { node => val constraints: Constraints = node.parent.map(_.record.element match { case e: ConstrainedHierarchicalTop => - Constraints(e.width, e.height, e.area, e.aspectRatio) + e.toConstraints case e: PlacedHierarchicalTop => - Constraints.sized(e.width, e.height) + Constraints() // These should be sized already case e: ConstrainedWeightedGrid => - val width = e.width(e.flatIndexOf(node.record.element.name)) - val height = e.height(e.flatIndexOf(node.record.element.name)) - val area = e.area(e.flatIndexOf(node.record.element.name)) - val aspectRatio = e.aspectRatio(e.flatIndexOf(node.record.element.name)) - Constraints(width, height, area, aspectRatio) + val (x, y) = e.indexOf(node.record.element.name).get + val xWeight = e.getXWeight(x) + val yWeight = e.getYWeight(y) + e.toConstraints.weightXY(xWeight, yWeight) case e: ConstrainedElasticGrid => - val width = e.width(e.flatIndexOf(node.record.element.name)) - val height = e.height(e.flatIndexOf(node.record.element.name)) - val area = e.area(e.flatIndexOf(node.record.element.name)) - val aspectRatio = e.aspectRatio(e.flatIndexOf(node.record.element.name)) - Constraints(width, height, area, aspectRatio) + val c = e.toConstraints + val widthConstraint = c.width match { + case x: Unconstrained => x + case x: Impossible => x + case x: Constrained => x.copy(eq = None, leq = x.eq.orElse(x.leq), geq = None, mof = None) + } + val heightConstraint = c.height match { + case x: Unconstrained => x + case x: Impossible => x + case x: Constrained => x.copy(eq = None, leq = x.eq.orElse(x.leq), geq = None, mof = None) + } + val areaConstraint = c.area match { + case x: Unconstrained => x + case x: Impossible => x + case x: Constrained => x.copy(eq = None, leq = x.eq.orElse(x.leq), geq = None, mof = None) + } + Constraints(width = widthConstraint, height = heightConstraint, area = areaConstraint, aspectRatio = Unconstrained()) case e: SizedGrid => val (x, y) = e.indexOf(node.record.element.name).get Constraints.sized(e.widths(x), e.heights(y)) @@ -57,59 +44,83 @@ class ConstraintPropagationPass(val topMod: String) extends Pass { Constraints() // These *should* be hard macros at this point, so no need to constrain them. Maybe aspect ratio? case _ => ??? // Many types can never be parents and shouldn't get here }).getOrElse(Constraints()) + // Only modify child - (None, Some(node.record.copy(element = node.record.element.applyConstraints(constraints)))) + val newElement = node.record.element match { + case e: Constrainable => + e.applyConstraints(constraints) + case e => ??? + } + Some(node.record.copy(element = newElement)) } + // Bottom-up pass tree.traverseMapPost { node => - // Get idx in parent - val idx = node.parent.map(_.record.element.flatIndexOf(node.record.element.name)).getOrElse(-1) - - val constraints: Constraints = node.record.element match { + node.record.element match { case e: ConstrainedSpacerRect => - Constraints(e.width, e.height, e.area, e.aspectRatio) + None case e: SizedSpacerRect => - Constraints.sized(e.width, e.height) + None case e: ConstrainedLogicRect => - Constraints(e.width, e.height, e.area, e.aspectRatio) + None case e: SizedLogicRect => - Constraints.sized(e.width, e.height) + None case e: PlacedLogicRect => - Constraints.sized(e.width, e.height) + None case e: ConstrainedHierarchicalTop => - Constraints(e.width, e.height, e.area, e.aspectRatio) + val newElement = e.applyConstraints(node.children(0).record.element.toConstraints) + Some(node.record.copy(element = newElement)) case e: PlacedHierarchicalTop => - Constraints.sized(e.width, e.height) + throw new Exception("Cannot propagate constraints to a PlacedHierarchicalTop") case e: ConstrainedWeightedGrid => - val width = e.width.reduce(_+_) - val height = e.height.reduce(_+_) - val area = e.area.reduce(_+_) - Constraints(width, height, area, Unconstrained()) + // TODO make this less repetitive with the below + // Preserve ordering of children and convert to 2d Seq + val children: Seq[Seq[Constraints]] = e.elements.map(_.map(x => tree.getRecord(x).element.toConstraints).getOrElse(Constraints())).grouped(e.xDim).toSeq + val widthConstraint = children.map(_.map(_.width).reduce(_ + _)).reduce(_ and _) + val heightConstraint = children.transpose.map(_.map(_.height).reduce(_ + _)).reduce(_ and _) + val areaConstraint = children.flatten.map(_.area).reduce(_ + _) + val newElement = e.applyConstraints(Constraints( + widthConstraint, + heightConstraint, + areaConstraint, + Unconstrained() + )) + // TODO handle the weights + Some(node.record.copy(element = newElement)) case e: ConstrainedElasticGrid => - val width = e.width.reduce(_+_) - val height = e.height.reduce(_+_) - val area = e.area.reduce(_+_) - Constraints(width, height, area, Unconstrained()) + // Preserve ordering of children and convert to 2d Seq + val children: Seq[Seq[Constraints]] = e.elements.map(_.map(x => tree.getRecord(x).element.toConstraints).getOrElse(Constraints())).grouped(e.xDim).toSeq + val widthConstraint = children.map(_.map(_.width).reduce(_ + _)).reduce(_ and _) + val heightConstraint = children.transpose.map(_.map(_.height).reduce(_ + _)).reduce(_ and _) + val areaConstraint = children.flatten.map(_.area).reduce(_ + _) + val newElement = e.applyConstraints(Constraints( + widthConstraint, + heightConstraint, + areaConstraint, + Unconstrained() + )) + Some(node.record.copy(element = newElement)) case e: SizedGrid => - Constraints.sized(e.widths.sum, e.heights.sum) + ??? // TODO probably should sanity check here case e: MemMacroArray => - Constraints(e.width, e.height, e.area, e.aspectRatio) + val area = node.children.map(_.record.element match { + case e2: HasFixedDimensions => e2.area + case e2 => throw new Exception("All macro dimensions must be resolved at this point") + }).sum + val newElement = e.applyConstraints(Constraints( + Unconstrained(), + Unconstrained(), + GreaterThanOrEqualTo(area), + Unconstrained() + )) + Some(node.record.copy(element = newElement)) case e: SizedMacro => - Constraints.sized(e.width, e.height) + None case e: PlacedMacro => - Constraints.sized(e.width, e.height) + None case _ => ??? } - - val newElementOpt = node.parent.map(_.record.element match { - case e: Grid => e.applyConstraintsTo(constraints, e.flatIndexOf(node.record.element.name)) - case e: ConstrainedHierarchicalTop => e.applyConstraints(constraints) - case e: PlacedHierarchicalTop => e.applyConstraints(constraints) - case e => e - }) - // Only modify parent - (newElementOpt.map(e => node.record.copy(element = e)), None) } // TODO propagate constraints to siblings diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanTree.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanTree.scala index 4c115404..1c5c89c9 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanTree.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanTree.scala @@ -73,21 +73,17 @@ class FloorplanTree(val state: FloorplanState, val topMod: String) { // (None, Some(record)) = modify node // (Some(record), None) = modify parent // (Some(r1), Some(r2)) = modify both - def traverseMapPre(f: (Node => (Option[FloorplanRecord], Option[FloorplanRecord]))) { traverseMapPreHelper(topNode, f) } - def traverseMapPost(f: (Node => (Option[FloorplanRecord], Option[FloorplanRecord]))) { traverseMapPostHelper(topNode, f) } + def traverseMapPre(f: (Node => Option[FloorplanRecord])) { traverseMapPreHelper(topNode, f) } + def traverseMapPost(f: (Node => Option[FloorplanRecord])) { traverseMapPostHelper(topNode, f) } - private def traverseMapPreHelper(n: Node, f: (Node => (Option[FloorplanRecord], Option[FloorplanRecord]))) { - val (parent, child) = f(n) - parent.foreach { r => n.parent.foreach(_.replace(r)) } - child.foreach { r => n.replace(r) } + private def traverseMapPreHelper(n: Node, f: (Node => Option[FloorplanRecord])) { + f(n).foreach { r => n.replace(r) } n.children.foreach { c => traverseMapPreHelper(c, f) } } - private def traverseMapPostHelper(n: Node, f: (Node => (Option[FloorplanRecord], Option[FloorplanRecord]))) { + private def traverseMapPostHelper(n: Node, f: (Node => Option[FloorplanRecord])) { n.children.foreach { c => traverseMapPostHelper(c, f) } - val (parent, child) = f(n) - parent.foreach { r => n.parent.foreach(_.replace(r)) } - child.foreach { r => n.replace(r) } + f(n).foreach { r => n.replace(r) } } def toState: FloorplanState = { diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceHierarchicalPass.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceHierarchicalPass.scala index 5f993700..101f1423 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceHierarchicalPass.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceHierarchicalPass.scala @@ -63,20 +63,20 @@ class ReplaceHierarchicalPass(val topMod: String) extends Pass { xDim = 1, yDim = 1, elements = Seq(Some(rename(tr.scope, t.name))), - width = Seq(t.width), - height = Seq(t.height), - area = Seq(t.area), - aspectRatio = Seq(t.aspectRatio) + width = t.width, + height = t.height, + area = t.area, + aspectRatio = t.aspectRatio ), ConstrainedElasticGrid( name = rename(tr.scope, t.name), parent = rename(r.scope, e.name), xDim = 1, yDim = 1, elements = Seq(Some(rename(tr.scope, t.topGroup))), - width = Seq(t.width), - height = Seq(t.height), - area = Seq(t.area), - aspectRatio = Seq(t.aspectRatio) + width = t.width, + height = t.height, + area = t.area, + aspectRatio = t.aspectRatio )) case t: PlacedHierarchicalTop => ??? // TODO not supported yet case _ => ??? From f00c30536192dc8318f1691bb6e261b36d31899f Mon Sep 17 00:00:00 2001 From: John Wright Date: Tue, 10 Aug 2021 03:10:35 -0700 Subject: [PATCH 58/73] Add basic mem array implementation --- .../barstools/floorplan/Constraints.scala | 99 ++++++++++++++++++- .../main/scala/barstools/floorplan/IR.scala | 2 - .../barstools/floorplan/chisel/API.scala | 7 +- .../floorplan/compiler/FloorplanTree.scala | 11 +++ .../barstools/floorplan/compiler/Pass.scala | 10 +- .../compiler/ReplaceMemMacroArrayPass.scala | 97 +++++++++++++++++- 6 files changed, 209 insertions(+), 17 deletions(-) diff --git a/floorplan/src/main/scala/barstools/floorplan/Constraints.scala b/floorplan/src/main/scala/barstools/floorplan/Constraints.scala index 2be959a3..9d015d3f 100644 --- a/floorplan/src/main/scala/barstools/floorplan/Constraints.scala +++ b/floorplan/src/main/scala/barstools/floorplan/Constraints.scala @@ -1,20 +1,42 @@ // See LICENSE for license details package barstools.floorplan -import scala.math.{BigInt, BigDecimal} +import scala.math.{BigInt, BigDecimal, sqrt} sealed trait Constraint { def and(that: Constraint): Constraint def +(that: Constraint): Constraint def *(that: BigDecimal): Constraint + def /(that: BigDecimal): Constraint def test(value: BigDecimal): Boolean + def minimize: Constraint + + def resolveMin: BigDecimal = this.minimize match { + case c: Impossible => throw new Exception("Cannot reduce impossible constraint. TODO provide more detailed debug info.") + case c: Unconstrained => BigDecimal(0) + case c: Constrained => + c.eq.foreach { x => return x } + c.geq.foreach { x => + c.mof.foreach { m => + val n = (x/m).setScale(0, BigDecimal.RoundingMode.UP) + m*n + } + return x + } + BigDecimal(0) + } + + def isConstrained: Boolean } final class Unconstrained extends Constraint { def and(that: Constraint) = that def +(that: Constraint) = that // ??? def *(that: BigDecimal) = this + def /(that: BigDecimal) = this def test(value: BigDecimal) = true + def minimize = this + def isConstrained = false } object Unconstrained { @@ -31,7 +53,10 @@ final class Impossible extends Constraint { def and(that: Constraint) = this def +(that: Constraint) = this def *(that: BigDecimal) = this + def /(that: BigDecimal) = this def test(value: BigDecimal) = false + def minimize = this + def isConstrained = true } object Impossible { @@ -40,12 +65,14 @@ object Impossible { } final case class Constrained( - eq: Option[BigDecimal], - geq: Option[BigDecimal], - leq: Option[BigDecimal], - mof: Option[BigDecimal] + eq: Option[BigDecimal] = None, + geq: Option[BigDecimal] = None, + leq: Option[BigDecimal] = None, + mof: Option[BigDecimal] = None ) extends Constraint { + def isConstrained: Boolean = eq.isDefined || geq.isDefined || leq.isDefined || mof.isDefined + def and(that: Constraint): Constraint = { that match { case that: Unconstrained => this @@ -170,6 +197,13 @@ final case class Constrained( mof = this.mof.map(_ * that) ) + def /(that: BigDecimal): Constraint = Constrained( + eq = this.eq.map(_ / that), + geq = this.geq.map(_ / that), + leq = this.leq.map(_ / that), + mof = this.mof.map(_ / that) + ) + def test(value: BigDecimal): Boolean = { val eqTest = this.eq.map(_ == value).getOrElse(true) val geqTest = this.geq.map(_ <= value).getOrElse(true) @@ -215,6 +249,61 @@ case class Constraints( area = this.area * (xWeight * yWeight), aspectRatio = this.aspectRatio * (yWeight / xWeight) ) + + def resolveMinDimensions(defaultWidth: BigDecimal, defaultHeight: BigDecimal): (BigDecimal, BigDecimal) = { + if (this.aspectRatio.isConstrained) { + if (this.area.isConstrained) { + // AspectRatio with Area + if (this.width == Unconstrained() && this.height == Unconstrained()) { + val heightConstraint = BigDecimal(sqrt((this.area.resolveMin * this.aspectRatio.resolveMin).doubleValue)) // TODO clean up rounding + (this.area.resolveMin / heightConstraint, heightConstraint) + } else { + // TODO resolve 3- or 4-way constraint (this is wrong) + ??? + } + } else { + // AspectRatio with no Area + // Use defaultWidth (TODO make this an option?) + if (this.width == Unconstrained() && this.height == Unconstrained()) { + (defaultWidth, this.aspectRatio.resolveMin * defaultWidth) + } else if (this.height == Unconstrained()) { + (this.height.resolveMin / this.aspectRatio.resolveMin, this.height.resolveMin) + } else if (this.width == Unconstrained()) { + (this.width.resolveMin, this.aspectRatio.resolveMin * this.width.resolveMin) + } else { + // TODO resolve 3-way constraint + ??? + } + } + } else { + if (this.area.isConstrained) { + // Area with no AspectRatio + // Use defaultWidth (TODO make this an option?) + if (this.width == Unconstrained() && this.height == Unconstrained()) { + (defaultWidth, this.area.resolveMin / defaultWidth) + } else if (this.height == Unconstrained()) { + (this.width.resolveMin, this.area.resolveMin / this.width.resolveMin) + } else if (this.width == Unconstrained()) { + (this.area.resolveMin / this.height.resolveMin, this.height.resolveMin) + } else { + // TODO resolve 3-way constraint (this is wrong) + ??? + } + } else { + // No Area or AspectRatio + val widthConstraint = this.width match { + case x: Unconstrained => defaultWidth + case x => x.resolveMin + } + val heightConstraint = this.height match { + case x: Unconstrained => defaultHeight + case x => x.resolveMin + } + (widthConstraint, heightConstraint) + } + } + } + } object Constraints { diff --git a/floorplan/src/main/scala/barstools/floorplan/IR.scala b/floorplan/src/main/scala/barstools/floorplan/IR.scala index 7710b82e..4fa08adf 100644 --- a/floorplan/src/main/scala/barstools/floorplan/IR.scala +++ b/floorplan/src/main/scala/barstools/floorplan/IR.scala @@ -136,8 +136,6 @@ private[floorplan] final case class ConstrainedSpacerRect( private[floorplan] final case class SizedSpacerRect( name: String, parent: String, - x: BigDecimal, - y: BigDecimal, width: BigDecimal, height: BigDecimal ) extends SizedRectPrimitive { diff --git a/floorplan/src/main/scala/barstools/floorplan/chisel/API.scala b/floorplan/src/main/scala/barstools/floorplan/chisel/API.scala index d57232b8..4866a49b 100644 --- a/floorplan/src/main/scala/barstools/floorplan/chisel/API.scala +++ b/floorplan/src/main/scala/barstools/floorplan/chisel/API.scala @@ -72,9 +72,9 @@ final class ChiselFloorplanContext private[chisel] (val scope: Target, val topEl ary } - def createMemArray(name: Option[String] = None): ChiselMemArray = { + def createMemArray(name: Option[String] = None, aspectRatio: Option[BigDecimal] = Some(BigDecimal(1))): ChiselMemArray = { val nameStr = FloorplanDatabase.getUnusedName(scope, name) - val elt = new ChiselMemArray(scope, nameStr, this) + val elt = new ChiselMemArray(scope, nameStr, aspectRatio, this) addElement(elt) } @@ -409,10 +409,11 @@ final class ChiselMem private[chisel] ( final class ChiselMemArray private[chisel] ( scope: Target, name: String, + aspectRatio: Option[BigDecimal], context: ChiselFloorplanContext ) extends ChiselArrayElement(scope, name, context) { protected def initialSize = 0 - protected def generateGroupElement(names: Seq[Option[String]]): Group = MemElementArray(name, parentName, names) + protected def generateGroupElement(names: Seq[Option[String]]): Group = MemElementArray(name, parentName, names, Unconstrained(), Unconstrained(), Unconstrained(), Constrained(eq = aspectRatio)) def addMem[T <: Data](mem: MemBase[T]) = this.addElement(this.context.addMem(mem)) } diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanTree.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanTree.scala index 1c5c89c9..de34dcf8 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanTree.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanTree.scala @@ -26,6 +26,16 @@ class FloorplanTree(val state: FloorplanState, val topMod: String) { def replace(r: FloorplanRecord) { _record = r } } + def getUniqueName(suggestion: String): String = { + var i = 0 + var tmp = suggestion + s"_${i}" + while (allNodes.keys.toSet.contains(tmp)) { + i = i + 1 + tmp = suggestion + s"_${i}" + } + tmp + } + def getRecord(s: String): FloorplanRecord = getNode(s).record def getNode(s: String): Node = allNodes(s) @@ -52,6 +62,7 @@ class FloorplanTree(val state: FloorplanState, val topMod: String) { case e: PlacedHierarchicalTop => e.elements.foreach(x => dfs(Some(n), _getRecord(x))) } + allNodes += (e.name -> n) n case e: Primitive => assert(parent.isDefined, "Must have parent") diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/Pass.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/Pass.scala index a289e4fb..a2ce5abd 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/Pass.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/Pass.scala @@ -13,9 +13,9 @@ object Pass { new OutOfBandAnnotationPass(sbAnnos), new ReplaceHierarchicalPass(opts.topMod), new ConstraintPropagationPass(opts.topMod), - new ReplaceMemMacroArrayPass, - new ResolveConstraintsPass, - new CalculatePlacementsPass + new ReplaceMemMacroArrayPass(opts.topMod), + new ResolveConstraintsPass(opts.topMod), + new CalculatePlacementsPass(opts.topMod) ) } } @@ -24,11 +24,11 @@ abstract class Pass { def execute(state: FloorplanState): FloorplanState } -class ResolveConstraintsPass extends Pass { +class ResolveConstraintsPass(topMod: String) extends Pass { def execute(state: FloorplanState): FloorplanState = state // TODO } -class CalculatePlacementsPass extends Pass { +class CalculatePlacementsPass(topMod: String) extends Pass { def execute(state: FloorplanState): FloorplanState = state // TODO } diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceMemMacroArrayPass.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceMemMacroArrayPass.scala index b3d7537a..d301c2c8 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceMemMacroArrayPass.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceMemMacroArrayPass.scala @@ -3,10 +3,103 @@ package barstools.floorplan.compiler import barstools.floorplan._ -class ReplaceMemMacroArrayPass extends Pass { +import scala.math.{BigDecimal, sqrt} + +class ReplaceMemMacroArrayPass(topMod: String) extends Pass { def execute(state: FloorplanState): FloorplanState = { + val tree = new FloorplanTree(state, topMod) // TODO this needs some additional options, etc. + val utilization = 0.8 // amount of area to devote to SRAMs + // For the proof-of-concept, we'll just replace them with an Array that meets whatever box constraints - state // TODO + val nodes = tree.allNodes.values.filter(_.record.element.isInstanceOf[MemMacroArray]) + + nodes.foreach { n => + val e = n.record.element.asInstanceOf[MemMacroArray] + // TODO this assumes that the MemMacroArray contains all the same type of SRAM. This is usually true, but we do nothing to enforce this. + + // Try to size based on the SRAM + val mem0 = tree.getRecord(e.elements(0).get).element.asInstanceOf[SizedMacro] + val filteredElements = e.elements.filter(_.isDefined) + val numMems = filteredElements.length + + val defaultWidth = (mem0.width * numMems) / utilization + val defaultHeight = mem0.height + val defaultArea = defaultWidth * defaultHeight + + val (width, height) = e.toConstraints.aspectRatio match { + case c: Unconstrained => + (defaultWidth, defaultHeight) + case c: Impossible => throw new Exception("Illegal impossible constraint") + case c: Constrained => + val h = c.eq.map(q => BigDecimal(sqrt((defaultArea * q).doubleValue))).getOrElse(defaultHeight) + (defaultArea/h, h) + } + + val stackHeight = (height / mem0.height).setScale(0, BigDecimal.RoundingMode.DOWN).toInt + val columns = (numMems + stackHeight - 1) / Seq(stackHeight, 1).max + val paddingColumns = (columns + 1) / 2 + val topPadding = height - (stackHeight*mem0.height) + val xPadding = (width - (columns*mem0.width)) / paddingColumns + val xDim = columns + paddingColumns + val yDim = stackHeight + 1 + + def addSpacer(w: BigDecimal, h: BigDecimal): Option[String] = { + val newElement = SizedSpacerRect( + name = tree.getUniqueName("spacer"), + parent = e.name, + width = w, + height = h + ) + n.addChildRecord(FloorplanRecord(n.record.scope, None, None, newElement)) + Some(newElement.name) + } + + val elements = Seq.tabulate(xDim*yDim) { i => + val col = (i % xDim) + val row = (i / xDim) + val group = 2*(col / 3) + (row*columns) + val spacerHeight = if (row == yDim-1) topPadding else mem0.height + if (col % 3 == 0) { + if (group >= numMems) { + addSpacer(xPadding, spacerHeight) + } else { + filteredElements(group) + } + } else if (col % 3 == 1) { + addSpacer(xPadding, spacerHeight) + } else { + if ((group + 1) >= numMems) { + addSpacer(xPadding, spacerHeight) + } else { + filteredElements(group + 1) + } + } + } + + val widths = Seq.tabulate(xDim) { i => + if (i % 3 == 0) { + mem0.width + } else if (i % 3 == 1) { + xPadding + } else { + mem0.width + } + } + val heights = Seq.fill(stackHeight)(mem0.height) ++ Seq(topPadding) + + val newElement = SizedGrid( + name = e.name, + parent = e.parent, + xDim = xDim, + yDim = yDim, + elements = elements, // TODO how to arrange these correctly? + widths = widths, + heights = heights + ) + n.replace(n.record.copy(element = newElement)) + } + + tree.toState } } From 526fb293e224479005a2981d7a7bcbd2af9609ef Mon Sep 17 00:00:00 2001 From: John Wright Date: Tue, 10 Aug 2021 10:05:36 -0700 Subject: [PATCH 59/73] Temporary commit, this compiles but we need to fix elements --- .../compiler/CalculatePlacementsPass.scala | 8 +++ .../compiler/ConstraintPropagationPass.scala | 44 ++++++++++++- .../barstools/floorplan/compiler/Pass.scala | 8 --- .../compiler/ResolveConstraintsPass.scala | 64 +++++++++++++++++++ 4 files changed, 115 insertions(+), 9 deletions(-) create mode 100644 floorplan/src/main/scala/barstools/floorplan/compiler/CalculatePlacementsPass.scala create mode 100644 floorplan/src/main/scala/barstools/floorplan/compiler/ResolveConstraintsPass.scala diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/CalculatePlacementsPass.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/CalculatePlacementsPass.scala new file mode 100644 index 00000000..4969a579 --- /dev/null +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/CalculatePlacementsPass.scala @@ -0,0 +1,8 @@ +// See LICENSE for license details +package barstools.floorplan.compiler + +import barstools.floorplan._ + +class CalculatePlacementsPass(topMod: String) extends Pass { + def execute(state: FloorplanState): FloorplanState = state // TODO +} diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/ConstraintPropagationPass.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/ConstraintPropagationPass.scala index d4a179f3..1afad673 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/ConstraintPropagationPass.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/ConstraintPropagationPass.scala @@ -123,7 +123,49 @@ class ConstraintPropagationPass(val topMod: String) extends Pass { } } - // TODO propagate constraints to siblings + // Sibling propagation to Grids + tree.allNodes.values.foreach { node => + node.record.element match { + case e: SizedGrid => + // Do nothing + case e: Grid => + // This look-up preserves ordering of children + val children = e.elements.map(_.map(x => tree.getNode(tree.getRecord(x).element.name))) + val wConstraints = Seq.tabulate(e.xDim)(iX => Seq.tabulate(e.yDim) { iY => + children(e.toIdx(iX, iY)).map(_.record.element.toConstraints.width).getOrElse(Unconstrained()) + } reduce (_ and _)) + val hConstraints = Seq.tabulate(e.yDim)(iY => Seq.tabulate(e.xDim) { iX => + children(e.toIdx(iX, iY)).map(_.record.element.toConstraints.height).getOrElse(Unconstrained()) + } reduce (_ and _)) + val newElements = children.zipWithIndex.map { case (child, idx) => + val (iX, iY) = e.fromIdx(idx) + if (child.isDefined) { + // We can always assume this is Constrainable- but really we should fix this in the type system + child.get.replace(child.get.record.copy( + element = child.get.record.element.asInstanceOf[Constrainable]. + applyConstraints(Constraints(width = wConstraints(iX), height = hConstraints(iY), Unconstrained(), Unconstrained())) + )) + Some(child.get.record.element.name) + } else { + val newElement = ConstrainedSpacerRect(tree.getUniqueName("spacer"), e.name). + applyConstraints(Constraints(width = wConstraints(iX), height = hConstraints(iY), Unconstrained(), Unconstrained())) + node.addChildRecord(FloorplanRecord(node.record.scope, None, None, newElement)) + Some(newElement.name) + } + } + // Needed to be able to use copy + e match { + case e: ConstrainedWeightedGrid => + node.replace(node.record.copy(element = e.copy(elements = newElements))) + case e: ConstrainedElasticGrid => + node.replace(node.record.copy(element = e.copy(elements = newElements))) + case e => + ??? + } + case e => + // Do nothing + } + } tree.toState } diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/Pass.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/Pass.scala index a2ce5abd..8198c541 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/Pass.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/Pass.scala @@ -24,11 +24,3 @@ abstract class Pass { def execute(state: FloorplanState): FloorplanState } -class ResolveConstraintsPass(topMod: String) extends Pass { - def execute(state: FloorplanState): FloorplanState = state // TODO -} - -class CalculatePlacementsPass(topMod: String) extends Pass { - def execute(state: FloorplanState): FloorplanState = state // TODO -} - diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/ResolveConstraintsPass.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/ResolveConstraintsPass.scala new file mode 100644 index 00000000..1d6413b7 --- /dev/null +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/ResolveConstraintsPass.scala @@ -0,0 +1,64 @@ +// See LICENSE for license details +package barstools.floorplan.compiler + +import barstools.floorplan._ + +class ResolveConstraintsPass(topMod: String) extends Pass { + def resolveConstraints(elt: ConstrainedRectLike): (BigDecimal, BigDecimal) = { + ??? + } + + def execute(state: FloorplanState): FloorplanState = { + val tree = new FloorplanTree(state, topMod) + + // TODO this isn't a correct implementation; it just resolves the constraints seen + // by any individual element rather than looking at the entirety of the floorplan + + /* + // Bottom-up pass + tree.traverseMapPost { node => + node.record.element match { + case e: SizedRectLike => e // Already sized + case e: ConstrainedSpacerRect => + val (width, height) = resolveConstraints(e) + SizedSpacerRect( + e.name, + e.parent, + width, + height + ) + case e: ConstrainedLogicRect => + val (width, height) = resolveConstraints(e) + SizedLogicRect( + e.name, + e.parent, + width, + height, + hardBoundary + ) + case e: ConstrainedHierarchicalTop => + val (width, height) = resolveConstraints(e) + PlacedHierarchicalTop( + e.name, + e.elements = Seq(e.topGroup), + width, + height, + e.margins, + e.hardBoundary + ) + case e: ConstrainedWeightedGrid => + // Preserve ordering of children and convert to 2d Seq + val children: Seq[Seq[Constraints]] = e.elements.map(_.map(x => tree.getRecord(x).element.toConstraints).getOrElse(Constraints())).grouped(e.xDim).toSeq + //SizedGrid(TODO) + ??? + case e: ConstrainedElasticGrid => + //SizedGrid(TODO) + ??? + case e => throw new Exception("Illegal element type") + } + } + */ + + tree.toState + } +} From 23d24caf81519b28edd58f00e5d04cc8f7590b0a Mon Sep 17 00:00:00 2001 From: John Wright Date: Tue, 10 Aug 2021 11:41:47 -0700 Subject: [PATCH 60/73] Remove options from elements arrays --- .../main/scala/barstools/floorplan/IR.scala | 25 ++++++----- .../barstools/floorplan/chisel/API.scala | 45 ++++++++++++++----- .../compiler/ConstraintPropagationPass.scala | 27 +++++------ .../floorplan/compiler/FloorplanTree.scala | 2 +- .../compiler/ReplaceHierarchicalPass.scala | 4 +- .../compiler/ReplaceMemMacroArrayPass.scala | 13 +++--- .../compiler/TransformMemsPass.scala | 2 +- 7 files changed, 67 insertions(+), 51 deletions(-) diff --git a/floorplan/src/main/scala/barstools/floorplan/IR.scala b/floorplan/src/main/scala/barstools/floorplan/IR.scala index 4fa08adf..619acfea 100644 --- a/floorplan/src/main/scala/barstools/floorplan/IR.scala +++ b/floorplan/src/main/scala/barstools/floorplan/IR.scala @@ -58,7 +58,7 @@ sealed abstract class Primitive extends ElementWithParent { } sealed abstract class Group extends ElementWithParent { - def elements: Seq[Option[String]] + def elements: Seq[String] def flatIndexOf(s: String): Int = elements.indexOf(Some(s)) } @@ -234,10 +234,11 @@ private[floorplan] final case class PlacedHierarchicalTop( sealed abstract class Grid extends Group { def xDim: Int def yDim: Int - def elements: Seq[Option[String]] + def elements: Seq[String] assert(xDim > 0, "X dimension of grid must be positive") assert(yDim > 0, "Y dimension of grid must be positive") + assert(elements.length == (xDim*yDim), s"Grid {name} has incorrect number of elements ({elements.length})") def toIdx(x: Int, y: Int): Int = xDim*y + x def fromIdx(i: Int): (Int, Int) = (i % xDim, i / xDim) @@ -255,7 +256,7 @@ private[floorplan] final case class ConstrainedWeightedGrid( parent: String, xDim: Int, yDim: Int, - elements: Seq[Option[String]], + elements: Seq[String], xWeights: Seq[BigDecimal], yWeights: Seq[BigDecimal], width: Constraint, @@ -268,7 +269,7 @@ private[floorplan] final case class ConstrainedWeightedGrid( this.copy( name = m(name), parent = m(parent), - elements = elements.map(_.map(m)) + elements = elements.map(m) ) } def applyConstraints(c: Constraints): Element = this.copy( @@ -287,7 +288,7 @@ private[floorplan] final case class ConstrainedElasticGrid( parent: String, xDim: Int, yDim: Int, - elements: Seq[Option[String]], + elements: Seq[String], width: Constraint, height: Constraint, area: Constraint, @@ -298,7 +299,7 @@ private[floorplan] final case class ConstrainedElasticGrid( this.copy( name = m(name), parent = m(parent), - elements = elements.map(_.map(m)) + elements = elements.map(m) ) } def applyConstraints(c: Constraints): Element = this.copy( @@ -314,7 +315,7 @@ private[floorplan] final case class SizedGrid( parent: String, xDim: Int, yDim: Int, - elements: Seq[Option[String]], + elements: Seq[String], widths: Seq[BigDecimal], heights: Seq[BigDecimal] ) extends Grid with SizedRectLike { @@ -323,7 +324,7 @@ private[floorplan] final case class SizedGrid( this.copy( name = m(name), parent = m(parent), - elements = elements.map(_.map(m)) + elements = elements.map(m) ) } def width: BigDecimal = widths.sum @@ -345,7 +346,7 @@ private[floorplan] final case class MemElement( private[floorplan] final case class MemElementArray( name: String, parent: String, - elements: Seq[Option[String]], + elements: Seq[String], width: Constraint = Unconstrained(), height: Constraint = Unconstrained(), area: Constraint = Unconstrained(), @@ -356,7 +357,7 @@ private[floorplan] final case class MemElementArray( this.copy( name = m(name), parent = m(parent), - elements = elements.map(_.map(m)) + elements = elements.map(m) ) } def applyConstraints(c: Constraints): Element = this.copy( @@ -374,7 +375,7 @@ private[floorplan] final case class MemElementArray( private[floorplan] final case class MemMacroArray( name: String, parent: String, - elements: Seq[Option[String]], + elements: Seq[String], width: Constraint = Unconstrained(), height: Constraint = Unconstrained(), area: Constraint = Unconstrained(), @@ -385,7 +386,7 @@ private[floorplan] final case class MemMacroArray( this.copy( name = m(name), parent = m(parent), - elements = elements.map(_.map(m)) + elements = elements.map(m) ) } def applyConstraints(c: Constraints): Element = this.copy( diff --git a/floorplan/src/main/scala/barstools/floorplan/chisel/API.scala b/floorplan/src/main/scala/barstools/floorplan/chisel/API.scala index 4866a49b..a701a73c 100644 --- a/floorplan/src/main/scala/barstools/floorplan/chisel/API.scala +++ b/floorplan/src/main/scala/barstools/floorplan/chisel/API.scala @@ -24,7 +24,13 @@ final class ChiselFloorplanContext private[chisel] (val scope: Target, val topEl e } - def elements: Seq[ChiselElement] = elementBuf.toSeq + def commit(): Seq[ChiselElement] = { + elementBuf.toSeq.collect { + case g: ChiselGroupElement => g.commit() + } + // Note that this is mutable and is different than above! + elementBuf.toSeq + } def createRect[T <: RawModule](module: T, width: Constraint = Unconstrained(), @@ -253,7 +259,7 @@ trait Direction { case Direction.Horizontal => h case Direction.Vertical => v } - def ifV[T](h: T, v: T): T = ifH(v, h) + def ifV[T](v: T, h: T): T = ifH(h, v) } object Direction { @@ -297,14 +303,25 @@ sealed abstract class ChiselGroupElement(scope: Target, name: String, val contex extends ChiselElement(scope, name) with CanBeParent { protected def initialSize: Int - protected val elements = Seq.fill(initialSize)(Option.empty[ChiselElement]).toBuffer + protected val elements = ArrayBuffer.fill(initialSize)(Option.empty[ChiselElement]) protected var isCommitted = false - protected def generateGroupElement(names: Seq[Option[String]]): Group + protected def generateGroupElement(names: Seq[String]): Group - protected def generateElement(): Group = { + private[chisel] def commit() { + // in scala 2.13 this is much cleaner: + //elements.mapInPlace(x => Some(x.getOrElse(context.createSpacer()))) + elements.zipWithIndex.foreach { case (elt, idx) => + if (elt.isEmpty) { + this._placeAt(idx, context.createSpacer()) + } + } isCommitted = true - generateGroupElement(elements.map(_.map(_.name))) + } + + protected def generateElement(): Group = { + assert(isCommitted) + generateGroupElement(elements.map(_.get.name)) } private[chisel] def _placeAt[T <: ChiselElement](idx: Int, e: T): T = { @@ -336,7 +353,13 @@ abstract class ChiselGridElement(scope: Target, name: String, context: ChiselFlo protected def initialSize = xDim * yDim protected def toIdx(x: Int, y: Int): Int = xDim*y + x - def placeAt[T <: ChiselElement](x: Int, y: Int, e: T): T = _placeAt(toIdx(x, y), e) + def placeAt[T <: ChiselElement](x: Int, y: Int, e: T): T = { + assert(x >= 0) + assert(y >= 0) + assert(x < xDim) + assert(y < yDim) + _placeAt(toIdx(x, y), e) + } } @@ -413,7 +436,7 @@ final class ChiselMemArray private[chisel] ( context: ChiselFloorplanContext ) extends ChiselArrayElement(scope, name, context) { protected def initialSize = 0 - protected def generateGroupElement(names: Seq[Option[String]]): Group = MemElementArray(name, parentName, names, Unconstrained(), Unconstrained(), Unconstrained(), Constrained(eq = aspectRatio)) + protected def generateGroupElement(names: Seq[String]): Group = MemElementArray(name, parentName, names, Unconstrained(), Unconstrained(), Unconstrained(), Constrained(eq = aspectRatio)) def addMem[T <: Data](mem: MemBase[T]) = this.addElement(this.context.addMem(mem)) } @@ -425,7 +448,7 @@ class ChiselElasticGrid private[chisel] ( xDim: Int, yDim: Int ) extends ChiselGridElement(scope, name, context, xDim, yDim) { - final protected def generateGroupElement(names: Seq[Option[String]]): Group = ConstrainedElasticGrid( + final protected def generateGroupElement(names: Seq[String]): Group = ConstrainedElasticGrid( name, parentName, xDim, @@ -444,7 +467,7 @@ class ChiselElasticArray private[chisel] ( dim: Int, val dir: Direction ) extends ChiselElasticGrid(scope, name, context, dir.ifH(dim,1), dir.ifV(dim,1)) { - def placeAt[T <: ChiselElement](i: Int, e: T): T = placeAt(dir.ifH(i,0), dir.ifV(0,i), e) + def placeAt[T <: ChiselElement](i: Int, e: T): T = super.placeAt(dir.ifH(i,0), dir.ifV(i,0), e) } class ChiselWeightedGrid private[chisel] ( @@ -456,7 +479,7 @@ class ChiselWeightedGrid private[chisel] ( xWeights: Seq[BigDecimal], yWeights: Seq[BigDecimal] ) extends ChiselGridElement(scope, name, context, xDim, yDim) { - final protected def generateGroupElement(names: Seq[Option[String]]): Group = ConstrainedWeightedGrid( + final protected def generateGroupElement(names: Seq[String]): Group = ConstrainedWeightedGrid( name, parentName, xDim, diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/ConstraintPropagationPass.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/ConstraintPropagationPass.scala index 1afad673..14d9663f 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/ConstraintPropagationPass.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/ConstraintPropagationPass.scala @@ -76,7 +76,7 @@ class ConstraintPropagationPass(val topMod: String) extends Pass { case e: ConstrainedWeightedGrid => // TODO make this less repetitive with the below // Preserve ordering of children and convert to 2d Seq - val children: Seq[Seq[Constraints]] = e.elements.map(_.map(x => tree.getRecord(x).element.toConstraints).getOrElse(Constraints())).grouped(e.xDim).toSeq + val children: Seq[Seq[Constraints]] = e.elements.map(x => tree.getRecord(x).element.toConstraints).grouped(e.xDim).toSeq val widthConstraint = children.map(_.map(_.width).reduce(_ + _)).reduce(_ and _) val heightConstraint = children.transpose.map(_.map(_.height).reduce(_ + _)).reduce(_ and _) val areaConstraint = children.flatten.map(_.area).reduce(_ + _) @@ -90,7 +90,7 @@ class ConstraintPropagationPass(val topMod: String) extends Pass { Some(node.record.copy(element = newElement)) case e: ConstrainedElasticGrid => // Preserve ordering of children and convert to 2d Seq - val children: Seq[Seq[Constraints]] = e.elements.map(_.map(x => tree.getRecord(x).element.toConstraints).getOrElse(Constraints())).grouped(e.xDim).toSeq + val children: Seq[Seq[Constraints]] = e.elements.map(x => tree.getRecord(x).element.toConstraints).grouped(e.xDim).toSeq val widthConstraint = children.map(_.map(_.width).reduce(_ + _)).reduce(_ and _) val heightConstraint = children.transpose.map(_.map(_.height).reduce(_ + _)).reduce(_ and _) val areaConstraint = children.flatten.map(_.area).reduce(_ + _) @@ -130,28 +130,21 @@ class ConstraintPropagationPass(val topMod: String) extends Pass { // Do nothing case e: Grid => // This look-up preserves ordering of children - val children = e.elements.map(_.map(x => tree.getNode(tree.getRecord(x).element.name))) + val children = e.elements.map(x => tree.getNode(tree.getRecord(x).element.name)) val wConstraints = Seq.tabulate(e.xDim)(iX => Seq.tabulate(e.yDim) { iY => - children(e.toIdx(iX, iY)).map(_.record.element.toConstraints.width).getOrElse(Unconstrained()) + children(e.toIdx(iX, iY)).record.element.toConstraints.width } reduce (_ and _)) val hConstraints = Seq.tabulate(e.yDim)(iY => Seq.tabulate(e.xDim) { iX => - children(e.toIdx(iX, iY)).map(_.record.element.toConstraints.height).getOrElse(Unconstrained()) + children(e.toIdx(iX, iY)).record.element.toConstraints.height } reduce (_ and _)) val newElements = children.zipWithIndex.map { case (child, idx) => val (iX, iY) = e.fromIdx(idx) - if (child.isDefined) { - // We can always assume this is Constrainable- but really we should fix this in the type system - child.get.replace(child.get.record.copy( - element = child.get.record.element.asInstanceOf[Constrainable]. - applyConstraints(Constraints(width = wConstraints(iX), height = hConstraints(iY), Unconstrained(), Unconstrained())) - )) - Some(child.get.record.element.name) - } else { - val newElement = ConstrainedSpacerRect(tree.getUniqueName("spacer"), e.name). + // We can always assume this is Constrainable- but really we should fix this in the type system + child.replace(child.record.copy( + element = child.record.element.asInstanceOf[Constrainable]. applyConstraints(Constraints(width = wConstraints(iX), height = hConstraints(iY), Unconstrained(), Unconstrained())) - node.addChildRecord(FloorplanRecord(node.record.scope, None, None, newElement)) - Some(newElement.name) - } + )) + child.record.element.name } // Needed to be able to use copy e match { diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanTree.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanTree.scala index de34dcf8..b46fe2fe 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanTree.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanTree.scala @@ -70,7 +70,7 @@ class FloorplanTree(val state: FloorplanState, val topMod: String) { case e: Group => assert(parent.isDefined, "Must have parent") val n = parent.get.addChildRecord(r) - e.elements.foreach(_.foreach(x => dfs(Some(n), _getRecord(x)))) + e.elements.foreach(x => dfs(Some(n), _getRecord(x))) n case _ => ??? } diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceHierarchicalPass.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceHierarchicalPass.scala index 101f1423..3b84a8b7 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceHierarchicalPass.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceHierarchicalPass.scala @@ -62,7 +62,7 @@ class ReplaceHierarchicalPass(val topMod: String) extends Pass { parent = rename(r.scope, e.parent), xDim = 1, yDim = 1, - elements = Seq(Some(rename(tr.scope, t.name))), + elements = Seq(rename(tr.scope, t.name)), width = t.width, height = t.height, area = t.area, @@ -72,7 +72,7 @@ class ReplaceHierarchicalPass(val topMod: String) extends Pass { parent = rename(r.scope, e.name), xDim = 1, yDim = 1, - elements = Seq(Some(rename(tr.scope, t.topGroup))), + elements = Seq(rename(tr.scope, t.topGroup)), width = t.width, height = t.height, area = t.area, diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceMemMacroArrayPass.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceMemMacroArrayPass.scala index d301c2c8..c9ff1661 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceMemMacroArrayPass.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceMemMacroArrayPass.scala @@ -19,9 +19,8 @@ class ReplaceMemMacroArrayPass(topMod: String) extends Pass { // TODO this assumes that the MemMacroArray contains all the same type of SRAM. This is usually true, but we do nothing to enforce this. // Try to size based on the SRAM - val mem0 = tree.getRecord(e.elements(0).get).element.asInstanceOf[SizedMacro] - val filteredElements = e.elements.filter(_.isDefined) - val numMems = filteredElements.length + val mem0 = tree.getRecord(e.elements(0)).element.asInstanceOf[SizedMacro] + val numMems = e.elements.length val defaultWidth = (mem0.width * numMems) / utilization val defaultHeight = mem0.height @@ -44,7 +43,7 @@ class ReplaceMemMacroArrayPass(topMod: String) extends Pass { val xDim = columns + paddingColumns val yDim = stackHeight + 1 - def addSpacer(w: BigDecimal, h: BigDecimal): Option[String] = { + def addSpacer(w: BigDecimal, h: BigDecimal): String = { val newElement = SizedSpacerRect( name = tree.getUniqueName("spacer"), parent = e.name, @@ -52,7 +51,7 @@ class ReplaceMemMacroArrayPass(topMod: String) extends Pass { height = h ) n.addChildRecord(FloorplanRecord(n.record.scope, None, None, newElement)) - Some(newElement.name) + newElement.name } val elements = Seq.tabulate(xDim*yDim) { i => @@ -64,7 +63,7 @@ class ReplaceMemMacroArrayPass(topMod: String) extends Pass { if (group >= numMems) { addSpacer(xPadding, spacerHeight) } else { - filteredElements(group) + e.elements(group) } } else if (col % 3 == 1) { addSpacer(xPadding, spacerHeight) @@ -72,7 +71,7 @@ class ReplaceMemMacroArrayPass(topMod: String) extends Pass { if ((group + 1) >= numMems) { addSpacer(xPadding, spacerHeight) } else { - filteredElements(group + 1) + e.elements(group + 1) } } } diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/TransformMemsPass.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/TransformMemsPass.scala index ca439e47..bb2429a0 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/TransformMemsPass.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/TransformMemsPass.scala @@ -51,7 +51,7 @@ class TransformMemsPass(instMap: Map[OfModule, Seq[(Instance, OfModule)]]) exten val element = MemMacroArray( name = e.name, parent = e.parent, - elements = e.elements.filter(_.isDefined).flatMap(x => renameMap(x.get)).map(x => Some(x)), + elements = e.elements.flatMap(x => renameMap(x)), width = e.width, height = e.height, area = e.area, From 5389eb9d7fc0d1ac5e694c441bcd4c76becd2554 Mon Sep 17 00:00:00 2001 From: John Wright Date: Tue, 10 Aug 2021 12:46:30 -0700 Subject: [PATCH 61/73] Add some basic implementations of resolve constraints --- .../barstools/floorplan/Constraints.scala | 6 +- .../compiler/ConstraintPropagationPass.scala | 295 +++++++++--------- .../compiler/ResolveConstraintsPass.scala | 60 +++- 3 files changed, 198 insertions(+), 163 deletions(-) diff --git a/floorplan/src/main/scala/barstools/floorplan/Constraints.scala b/floorplan/src/main/scala/barstools/floorplan/Constraints.scala index 9d015d3f..45ff9fcc 100644 --- a/floorplan/src/main/scala/barstools/floorplan/Constraints.scala +++ b/floorplan/src/main/scala/barstools/floorplan/Constraints.scala @@ -250,6 +250,8 @@ case class Constraints( aspectRatio = this.aspectRatio * (yWeight / xWeight) ) + def resolveMinDimensions(): (BigDecimal, BigDecimal) = resolveMinDimensions(BigDecimal(0), BigDecimal(0)) + def resolveMinDimensions(defaultWidth: BigDecimal, defaultHeight: BigDecimal): (BigDecimal, BigDecimal) = { if (this.aspectRatio.isConstrained) { if (this.area.isConstrained) { @@ -287,7 +289,9 @@ case class Constraints( (this.area.resolveMin / this.height.resolveMin, this.height.resolveMin) } else { // TODO resolve 3-way constraint (this is wrong) - ??? + val widthReq = Seq(this.width.resolveMin, this.area.resolveMin / this.height.resolveMin).max + val heightReq = Seq(this.width.resolveMin, this.area.resolveMin / this.width.resolveMin).max + (widthReq, heightReq) } } else { // No Area or AspectRatio diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/ConstraintPropagationPass.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/ConstraintPropagationPass.scala index 14d9663f..6cb54676 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/ConstraintPropagationPass.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/ConstraintPropagationPass.scala @@ -7,158 +7,163 @@ class ConstraintPropagationPass(val topMod: String) extends Pass { def execute(state: FloorplanState): FloorplanState = { val tree = new FloorplanTree(state, topMod) - // Top-down pass - tree.traverseMapPre { node => - val constraints: Constraints = node.parent.map(_.record.element match { - case e: ConstrainedHierarchicalTop => - e.toConstraints - case e: PlacedHierarchicalTop => - Constraints() // These should be sized already - case e: ConstrainedWeightedGrid => - val (x, y) = e.indexOf(node.record.element.name).get - val xWeight = e.getXWeight(x) - val yWeight = e.getYWeight(y) - e.toConstraints.weightXY(xWeight, yWeight) - case e: ConstrainedElasticGrid => - val c = e.toConstraints - val widthConstraint = c.width match { - case x: Unconstrained => x - case x: Impossible => x - case x: Constrained => x.copy(eq = None, leq = x.eq.orElse(x.leq), geq = None, mof = None) - } - val heightConstraint = c.height match { - case x: Unconstrained => x - case x: Impossible => x - case x: Constrained => x.copy(eq = None, leq = x.eq.orElse(x.leq), geq = None, mof = None) - } - val areaConstraint = c.area match { - case x: Unconstrained => x - case x: Impossible => x - case x: Constrained => x.copy(eq = None, leq = x.eq.orElse(x.leq), geq = None, mof = None) - } - Constraints(width = widthConstraint, height = heightConstraint, area = areaConstraint, aspectRatio = Unconstrained()) - case e: SizedGrid => - val (x, y) = e.indexOf(node.record.element.name).get - Constraints.sized(e.widths(x), e.heights(y)) - case e: MemMacroArray => - Constraints() // These *should* be hard macros at this point, so no need to constrain them. Maybe aspect ratio? - case _ => ??? // Many types can never be parents and shouldn't get here - }).getOrElse(Constraints()) + // Iterate a couple times (this is a hack FIXME) + val nIter = 3 + for (iter <- (0 until nIter)) { + // Top-down pass + tree.traverseMapPre { node => + val constraints: Constraints = node.parent.map(_.record.element match { + case e: ConstrainedHierarchicalTop => + e.toConstraints + case e: PlacedHierarchicalTop => + Constraints() // These should be sized already + case e: ConstrainedWeightedGrid => + val (x, y) = e.indexOf(node.record.element.name).get + val xWeight = e.getXWeight(x) + val yWeight = e.getYWeight(y) + e.toConstraints.weightXY(xWeight, yWeight) + case e: ConstrainedElasticGrid => + val c = e.toConstraints + val widthConstraint = c.width match { + case x: Unconstrained => x + case x: Impossible => x + case x: Constrained => x.copy(eq = None, leq = x.eq.orElse(x.leq), geq = None, mof = None) + } + val heightConstraint = c.height match { + case x: Unconstrained => x + case x: Impossible => x + case x: Constrained => x.copy(eq = None, leq = x.eq.orElse(x.leq), geq = None, mof = None) + } + val areaConstraint = c.area match { + case x: Unconstrained => x + case x: Impossible => x + case x: Constrained => x.copy(eq = None, leq = x.eq.orElse(x.leq), geq = None, mof = None) + } + Constraints(width = widthConstraint, height = heightConstraint, area = areaConstraint, aspectRatio = Unconstrained()) + case e: SizedGrid => + val (x, y) = e.indexOf(node.record.element.name).get + Constraints.sized(e.widths(x), e.heights(y)) + case e: MemMacroArray => + Constraints() // These *should* be hard macros at this point, so no need to constrain them. Maybe aspect ratio? + case _ => ??? // Many types can never be parents and shouldn't get here + }).getOrElse(Constraints()) - // Only modify child - val newElement = node.record.element match { - case e: Constrainable => - e.applyConstraints(constraints) - case e => ??? + // Only modify child + val newElement = node.record.element match { + case e: Constrainable => + e.applyConstraints(constraints) + case e => ??? + } + Some(node.record.copy(element = newElement)) } - Some(node.record.copy(element = newElement)) - } - // Bottom-up pass - tree.traverseMapPost { node => - node.record.element match { - case e: ConstrainedSpacerRect => - None - case e: SizedSpacerRect => - None - case e: ConstrainedLogicRect => - None - case e: SizedLogicRect => - None - case e: PlacedLogicRect => - None - case e: ConstrainedHierarchicalTop => - val newElement = e.applyConstraints(node.children(0).record.element.toConstraints) - Some(node.record.copy(element = newElement)) - case e: PlacedHierarchicalTop => - throw new Exception("Cannot propagate constraints to a PlacedHierarchicalTop") - case e: ConstrainedWeightedGrid => - // TODO make this less repetitive with the below - // Preserve ordering of children and convert to 2d Seq - val children: Seq[Seq[Constraints]] = e.elements.map(x => tree.getRecord(x).element.toConstraints).grouped(e.xDim).toSeq - val widthConstraint = children.map(_.map(_.width).reduce(_ + _)).reduce(_ and _) - val heightConstraint = children.transpose.map(_.map(_.height).reduce(_ + _)).reduce(_ and _) - val areaConstraint = children.flatten.map(_.area).reduce(_ + _) - val newElement = e.applyConstraints(Constraints( - widthConstraint, - heightConstraint, - areaConstraint, - Unconstrained() - )) - // TODO handle the weights - Some(node.record.copy(element = newElement)) - case e: ConstrainedElasticGrid => - // Preserve ordering of children and convert to 2d Seq - val children: Seq[Seq[Constraints]] = e.elements.map(x => tree.getRecord(x).element.toConstraints).grouped(e.xDim).toSeq - val widthConstraint = children.map(_.map(_.width).reduce(_ + _)).reduce(_ and _) - val heightConstraint = children.transpose.map(_.map(_.height).reduce(_ + _)).reduce(_ and _) - val areaConstraint = children.flatten.map(_.area).reduce(_ + _) - val newElement = e.applyConstraints(Constraints( - widthConstraint, - heightConstraint, - areaConstraint, - Unconstrained() - )) - Some(node.record.copy(element = newElement)) - case e: SizedGrid => - ??? // TODO probably should sanity check here - case e: MemMacroArray => - val area = node.children.map(_.record.element match { - case e2: HasFixedDimensions => e2.area - case e2 => throw new Exception("All macro dimensions must be resolved at this point") - }).sum - val newElement = e.applyConstraints(Constraints( - Unconstrained(), - Unconstrained(), - GreaterThanOrEqualTo(area), - Unconstrained() - )) - Some(node.record.copy(element = newElement)) - case e: SizedMacro => - None - case e: PlacedMacro => - None - case _ => ??? + // Bottom-up pass + tree.traverseMapPost { node => + node.record.element match { + case e: ConstrainedSpacerRect => + None + case e: SizedSpacerRect => + None + case e: ConstrainedLogicRect => + None + case e: SizedLogicRect => + None + case e: PlacedLogicRect => + None + case e: ConstrainedHierarchicalTop => + val newElement = e.applyConstraints(node.children(0).record.element.toConstraints) + Some(node.record.copy(element = newElement)) + case e: PlacedHierarchicalTop => + throw new Exception("Cannot propagate constraints to a PlacedHierarchicalTop") + case e: ConstrainedWeightedGrid => + // TODO make this less repetitive with the below + // Preserve ordering of children and convert to 2d Seq + val children: Seq[Seq[Constraints]] = e.elements.map(x => tree.getRecord(x).element.toConstraints).grouped(e.xDim).toSeq + val widthConstraint = children.map(_.map(_.width).reduce(_ + _)).reduce(_ and _) + val heightConstraint = children.transpose.map(_.map(_.height).reduce(_ + _)).reduce(_ and _) + val areaConstraint = children.flatten.map(_.area).reduce(_ + _) + val newElement = e.applyConstraints(Constraints( + widthConstraint, + heightConstraint, + areaConstraint, + Unconstrained() + )) + // TODO handle the weights + Some(node.record.copy(element = newElement)) + case e: ConstrainedElasticGrid => + // Preserve ordering of children and convert to 2d Seq + val children: Seq[Seq[Constraints]] = e.elements.map(x => tree.getRecord(x).element.toConstraints).grouped(e.xDim).toSeq + val widthConstraint = children.map(_.map(_.width).reduce(_ + _)).reduce(_ and _) + val heightConstraint = children.transpose.map(_.map(_.height).reduce(_ + _)).reduce(_ and _) + val areaConstraint = children.flatten.map(_.area).reduce(_ + _) + val newElement = e.applyConstraints(Constraints( + widthConstraint, + heightConstraint, + areaConstraint, + Unconstrained() + )) + Some(node.record.copy(element = newElement)) + case e: SizedGrid => + ??? // TODO probably should sanity check here + case e: MemMacroArray => + val area = node.children.map(_.record.element match { + case e2: HasFixedDimensions => e2.area + case e2 => throw new Exception("All macro dimensions must be resolved at this point") + }).sum + val newElement = e.applyConstraints(Constraints( + Unconstrained(), + Unconstrained(), + GreaterThanOrEqualTo(area), + Unconstrained() + )) + Some(node.record.copy(element = newElement)) + case e: SizedMacro => + None + case e: PlacedMacro => + None + case _ => ??? + } } - } - // Sibling propagation to Grids - tree.allNodes.values.foreach { node => - node.record.element match { - case e: SizedGrid => - // Do nothing - case e: Grid => - // This look-up preserves ordering of children - val children = e.elements.map(x => tree.getNode(tree.getRecord(x).element.name)) - val wConstraints = Seq.tabulate(e.xDim)(iX => Seq.tabulate(e.yDim) { iY => - children(e.toIdx(iX, iY)).record.element.toConstraints.width - } reduce (_ and _)) - val hConstraints = Seq.tabulate(e.yDim)(iY => Seq.tabulate(e.xDim) { iX => - children(e.toIdx(iX, iY)).record.element.toConstraints.height - } reduce (_ and _)) - val newElements = children.zipWithIndex.map { case (child, idx) => - val (iX, iY) = e.fromIdx(idx) - // We can always assume this is Constrainable- but really we should fix this in the type system - child.replace(child.record.copy( - element = child.record.element.asInstanceOf[Constrainable]. - applyConstraints(Constraints(width = wConstraints(iX), height = hConstraints(iY), Unconstrained(), Unconstrained())) - )) - child.record.element.name - } - // Needed to be able to use copy - e match { - case e: ConstrainedWeightedGrid => - node.replace(node.record.copy(element = e.copy(elements = newElements))) - case e: ConstrainedElasticGrid => - node.replace(node.record.copy(element = e.copy(elements = newElements))) - case e => - ??? - } - case e => - // Do nothing + // Sibling propagation to Grids + tree.allNodes.values.foreach { node => + node.record.element match { + case e: SizedGrid => + // Do nothing + case e: Grid => + // This look-up preserves ordering of children + val children = e.elements.map(x => tree.getNode(tree.getRecord(x).element.name)) + val wConstraints = Seq.tabulate(e.xDim)(iX => Seq.tabulate(e.yDim) { iY => + children(e.toIdx(iX, iY)).record.element.toConstraints.width + } reduce (_ and _)) + val hConstraints = Seq.tabulate(e.yDim)(iY => Seq.tabulate(e.xDim) { iX => + children(e.toIdx(iX, iY)).record.element.toConstraints.height + } reduce (_ and _)) + val newElements = children.zipWithIndex.map { case (child, idx) => + val (iX, iY) = e.fromIdx(idx) + // We can always assume this is Constrainable- but really we should fix this in the type system + child.replace(child.record.copy( + element = child.record.element.asInstanceOf[Constrainable]. + applyConstraints(Constraints(width = wConstraints(iX), height = hConstraints(iY), Unconstrained(), Unconstrained())) + )) + child.record.element.name + } + // Needed to be able to use copy + e match { + case e: ConstrainedWeightedGrid => + node.replace(node.record.copy(element = e.copy(elements = newElements))) + case e: ConstrainedElasticGrid => + node.replace(node.record.copy(element = e.copy(elements = newElements))) + case e => + ??? + } + case e => + // Do nothing + } } - } + + } // End of iteration loop tree.toState } diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/ResolveConstraintsPass.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/ResolveConstraintsPass.scala index 1d6413b7..abb1a867 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/ResolveConstraintsPass.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/ResolveConstraintsPass.scala @@ -5,7 +5,8 @@ import barstools.floorplan._ class ResolveConstraintsPass(topMod: String) extends Pass { def resolveConstraints(elt: ConstrainedRectLike): (BigDecimal, BigDecimal) = { - ??? + // TODO this is a temporary strategy + elt.toConstraints.resolveMinDimensions() } def execute(state: FloorplanState): FloorplanState = { @@ -14,50 +15,75 @@ class ResolveConstraintsPass(topMod: String) extends Pass { // TODO this isn't a correct implementation; it just resolves the constraints seen // by any individual element rather than looking at the entirety of the floorplan - /* // Bottom-up pass tree.traverseMapPost { node => node.record.element match { - case e: SizedRectLike => e // Already sized + case e: SizedRectLike => None // Already sized case e: ConstrainedSpacerRect => val (width, height) = resolveConstraints(e) - SizedSpacerRect( + Some(node.record.copy(element = SizedSpacerRect( e.name, e.parent, width, height - ) + ))) case e: ConstrainedLogicRect => val (width, height) = resolveConstraints(e) - SizedLogicRect( + Some(node.record.copy(element = SizedLogicRect( e.name, e.parent, width, height, - hardBoundary - ) + e.hardBoundary + ))) case e: ConstrainedHierarchicalTop => val (width, height) = resolveConstraints(e) - PlacedHierarchicalTop( + Some(node.record.copy(element = PlacedHierarchicalTop( e.name, - e.elements = Seq(e.topGroup), + Seq(e.topGroup), width, height, e.margins, e.hardBoundary - ) + ))) case e: ConstrainedWeightedGrid => + // TODO make not repetitive // Preserve ordering of children and convert to 2d Seq - val children: Seq[Seq[Constraints]] = e.elements.map(_.map(x => tree.getRecord(x).element.toConstraints).getOrElse(Constraints())).grouped(e.xDim).toSeq - //SizedGrid(TODO) - ??? + // FIXME this is a hack that assumes all of the elements are equally constrained in a row (height) or column (width) + // This is true for the current implementation of the constraints propagation but not generally true + val childDims: Seq[(BigDecimal, BigDecimal)] = e.elements.map(x => tree.getRecord(x).element.toConstraints.resolveMinDimensions()) + val widths = childDims.take(e.xDim).map(_._1) + val heights = childDims.grouped(e.xDim).map(_(0)._2).toSeq + + Some(node.record.copy(element = SizedGrid( + e.name, + e.parent, + e.xDim, + e.yDim, + e.elements, + widths, + heights + ))) case e: ConstrainedElasticGrid => - //SizedGrid(TODO) - ??? + // Preserve ordering of children and convert to 2d Seq + // FIXME this is a hack that assumes all of the elements are equally constrained in a row (height) or column (width) + // This is true for the current implementation of the constraints propagation but not generally true + val childDims: Seq[(BigDecimal, BigDecimal)] = e.elements.map(x => tree.getRecord(x).element.toConstraints.resolveMinDimensions()) + val widths = childDims.take(e.xDim).map(_._1) + val heights = childDims.grouped(e.xDim).map(_(0)._2).toSeq + + Some(node.record.copy(element = SizedGrid( + e.name, + e.parent, + e.xDim, + e.yDim, + e.elements, + widths, + heights + ))) case e => throw new Exception("Illegal element type") } } - */ tree.toState } From 466ae304827213d6ceafbf7b7694431667b24e75 Mon Sep 17 00:00:00 2001 From: John Wright Date: Tue, 10 Aug 2021 13:06:01 -0700 Subject: [PATCH 62/73] Comment cleanup --- .../barstools/floorplan/compiler/FloorplanTree.scala | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanTree.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanTree.scala index b46fe2fe..9713467c 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanTree.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanTree.scala @@ -79,11 +79,9 @@ class FloorplanTree(val state: FloorplanState, val topMod: String) { val topNode = dfs(None, topRecord) // Traverse using DFS, passing the node to a function which expects an - // (Option[FloorplanRecord], Option[FloorplanRecord]) return - // (None, None) = do no modify - // (None, Some(record)) = modify node - // (Some(record), None) = modify parent - // (Some(r1), Some(r2)) = modify both + // Option[FloorplanRecord] return + // None = do no modify + // Some(record) = modify node def traverseMapPre(f: (Node => Option[FloorplanRecord])) { traverseMapPreHelper(topNode, f) } def traverseMapPost(f: (Node => Option[FloorplanRecord])) { traverseMapPostHelper(topNode, f) } From 322c17ebd255196963f37c40764bc2d752c18fcd Mon Sep 17 00:00:00 2001 From: John Wright Date: Tue, 10 Aug 2021 14:30:43 -0700 Subject: [PATCH 63/73] First pass of CalculatePlacementsPass --- .../main/scala/barstools/floorplan/IR.scala | 17 +++- .../compiler/CalculatePlacementsPass.scala | 82 ++++++++++++++++++- .../compiler/ConstraintPropagationPass.scala | 4 + .../floorplan/compiler/FloorplanTree.scala | 56 ++++++++++--- .../compiler/OutOfBandAnnotationPass.scala | 7 ++ .../compiler/ReplaceHierarchicalPass.scala | 2 + .../compiler/ResolveConstraintsPass.scala | 4 +- 7 files changed, 156 insertions(+), 16 deletions(-) diff --git a/floorplan/src/main/scala/barstools/floorplan/IR.scala b/floorplan/src/main/scala/barstools/floorplan/IR.scala index 619acfea..77092acd 100644 --- a/floorplan/src/main/scala/barstools/floorplan/IR.scala +++ b/floorplan/src/main/scala/barstools/floorplan/IR.scala @@ -214,16 +214,29 @@ private[floorplan] final case class ConstrainedHierarchicalTop( ) } +private[floorplan] final case class SizedHierarchicalTop( + name: String, + topGroup: String, + width: BigDecimal, + height: BigDecimal, + margins: Margins, + hardBoundary: Boolean +) extends Top with SizedRectLike { + final def level = 1 + def mapNames(m: (String) => String): Element = this.copy(name = m(name), topGroup = m(topGroup)) + def flatIndexOf(s: String): Int = if (topGroup == s) 0 else -1 +} + private[floorplan] final case class PlacedHierarchicalTop( name: String, elements: Seq[String], + x: BigDecimal, + y: BigDecimal, width: BigDecimal, height: BigDecimal, margins: Margins, hardBoundary: Boolean ) extends Top with PlacedRectLike { - final def x = BigDecimal(0) - final def y = BigDecimal(0) final def level = 0 def mapNames(m: (String) => String): Element = this.copy(name = m(name), elements.map(m)) def flatIndexOf(s: String): Int = elements.indexOf(s) diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/CalculatePlacementsPass.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/CalculatePlacementsPass.scala index 4969a579..d0a06aa3 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/CalculatePlacementsPass.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/CalculatePlacementsPass.scala @@ -3,6 +3,86 @@ package barstools.floorplan.compiler import barstools.floorplan._ +import scala.collection.mutable.{ArrayBuffer} + class CalculatePlacementsPass(topMod: String) extends Pass { - def execute(state: FloorplanState): FloorplanState = state // TODO + def execute(state: FloorplanState): FloorplanState = { + val tree = new FloorplanTree(state, topMod) + + val top = tree.topNode + assert(top.record.element.isInstanceOf[SizedHierarchicalTop], "PlacedHierarchicalTop not yet supported pre-CalculatePlacementsPass. Use SizedHierarchicalTop instead.") + + val elementBuf = new ArrayBuffer[Element]() + + // Custom traversal TODO should this go in the FloorplanTree code somehow? + def traverse(n: FloorplanTreeNode, x: BigDecimal, y: BigDecimal, placeTopOpt: Option[FloorplanTreeNode]) { + n.record.element match { + case e: SizedHierarchicalTop => + assert(placeTopOpt.isEmpty) + // Note: we intentionally wait to add this until the end so we know all of the elements + val children = n.children + assert(children.length == 1) // should only be a topGroup here + children.foreach { cNode => traverse(cNode, x, y, Some(n)) } + n.replace(n.record.copy(element = PlacedHierarchicalTop( + e.name, + elementBuf.toSeq.map(_.name), + x, + y, + e.width, + e.height, + e.margins, + e.hardBoundary + ))) + case e: SizedGrid => + assert(placeTopOpt.isDefined) + // traverse down + val nodes: Seq[FloorplanTreeNode] = e.elements.map(name => tree.getNode(name)) + val children: Seq[SizedRectLike] = nodes.map(_.record.element.asInstanceOf[SizedRectLike]) + val widths: Seq[BigDecimal] = children.take(e.xDim).map(_.width).scanLeft(BigDecimal(0))(_+_).takeRight(e.xDim) + val heights: Seq[BigDecimal] = children.grouped(e.xDim).map(_(0).height).toSeq.scanLeft(BigDecimal(0))(_+_).takeRight(e.yDim) + + nodes.zipWithIndex.foreach { case (node, idx) => + val (iX, iY) = e.fromIdx(idx) + traverse(node, x + widths(iX), y + heights(iY), placeTopOpt) + } + + // delete it + n.delete() + case e: SizedLogicRect => + assert(placeTopOpt.isDefined) + n.replace(n.record.copy(element = PlacedLogicRect( + e.name, + e.parent, + x, + y, + e.width, + e.height, + e.hardBoundary + ))) + n.reparent(placeTopOpt.get) + elementBuf.append(n.record.element) + case e: SizedSpacerRect => + assert(placeTopOpt.isDefined) + // delete it + n.delete() + case e: SizedMacro => + assert(placeTopOpt.isDefined) + n.replace(n.record.copy(element = PlacedMacro( + e.name, + e.parent, + x, + y, + e.width, + e.height + ))) + n.reparent(placeTopOpt.get) + elementBuf.append(n.record.element) + case e => ??? // Shouldn't get here + } + } + + traverse(top, BigDecimal(0), BigDecimal(0), None) + + tree.toState + } } diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/ConstraintPropagationPass.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/ConstraintPropagationPass.scala index 6cb54676..d5fc62c2 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/ConstraintPropagationPass.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/ConstraintPropagationPass.scala @@ -15,6 +15,8 @@ class ConstraintPropagationPass(val topMod: String) extends Pass { val constraints: Constraints = node.parent.map(_.record.element match { case e: ConstrainedHierarchicalTop => e.toConstraints + case e: SizedHierarchicalTop => + Constraints() // These should be sized already case e: PlacedHierarchicalTop => Constraints() // These should be sized already case e: ConstrainedWeightedGrid => @@ -74,6 +76,8 @@ class ConstraintPropagationPass(val topMod: String) extends Pass { case e: ConstrainedHierarchicalTop => val newElement = e.applyConstraints(node.children(0).record.element.toConstraints) Some(node.record.copy(element = newElement)) + case e: SizedHierarchicalTop => + throw new Exception("Cannot propagate constraints to a SizedHierarchicalTop") case e: PlacedHierarchicalTop => throw new Exception("Cannot propagate constraints to a PlacedHierarchicalTop") case e: ConstrainedWeightedGrid => diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanTree.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanTree.scala index 9713467c..76da7dc0 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanTree.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanTree.scala @@ -4,28 +4,60 @@ package barstools.floorplan.compiler import scala.collection.mutable.{ArrayBuffer, HashMap} import barstools.floorplan._ +// TODO this is required to give access to Node types outside FloorplanTree. Ideally we should move all that functionality into this class +// with some type-generic methods, but for now this works +sealed trait FloorplanTreeNode { + + def parent: Option[FloorplanTreeNode] + def children: Seq[FloorplanTreeNode] + def record: FloorplanRecord + def addChildRecord(cRecord: FloorplanRecord): FloorplanTreeNode + def removeChild(n: FloorplanTreeNode): Unit + def delete(): Unit + def replace(r: FloorplanRecord): Unit + def reparent(p: FloorplanTreeNode): Unit + +} + class FloorplanTree(val state: FloorplanState, val topMod: String) { - val allNodes = new HashMap[String, Node]() + val allNodes = new HashMap[String, FloorplanTreeNode]() - class Node(val parent: Option[Node], initialRecord: FloorplanRecord) { - val children = new ArrayBuffer[Node]() + class Node(val parent: Option[FloorplanTreeNode], initialRecord: FloorplanRecord) extends FloorplanTreeNode { + val _children = new ArrayBuffer[FloorplanTreeNode]() // TODO this might be dangerous private var _record = initialRecord + def children = _children.toSeq def record = _record - def addChildRecord(cRecord: FloorplanRecord): Node = { + def addChildRecord(cRecord: FloorplanRecord): FloorplanTreeNode = { val n = new Node(Some(this), cRecord) - children += n + _children += n allNodes += (cRecord.element.name -> n) n } + def removeChild(n: FloorplanTreeNode) { + _children.remove(_children.indexOf(n)) + } + + def delete() { + assert(_children.isEmpty) // sanity check that we aren't orphaning nodes + parent.foreach(_.removeChild(this)) + allNodes.remove(record.element.name) + } + def replace(r: FloorplanRecord) { _record = r } + + def reparent(p: FloorplanTreeNode) { + this.delete() + p.addChildRecord(record) + } } + def getUniqueName(suggestion: String): String = { var i = 0 var tmp = suggestion + s"_${i}" @@ -37,7 +69,7 @@ class FloorplanTree(val state: FloorplanState, val topMod: String) { } def getRecord(s: String): FloorplanRecord = getNode(s).record - def getNode(s: String): Node = allNodes(s) + def getNode(s: String): FloorplanTreeNode = allNodes(s) // These are only used by the constructor private val allRecords: Map[String, FloorplanRecord] = state.records.map({ x => (x.element.name -> x) }).toMap @@ -50,7 +82,7 @@ class FloorplanTree(val state: FloorplanState, val topMod: String) { assert(topRecords.length == 1, "Must be exactly one Top record") val topRecord = topRecords(0) - private def dfs(parent: Option[Node], r: FloorplanRecord): Node = { + private def dfs(parent: Option[FloorplanTreeNode], r: FloorplanRecord): FloorplanTreeNode = { r.element match { case e: Top => assert(!parent.isDefined, "Cannot have multiple tops") @@ -59,6 +91,8 @@ class FloorplanTree(val state: FloorplanState, val topMod: String) { e match { case e: ConstrainedHierarchicalTop => dfs(Some(n), _getRecord(e.topGroup)) + case e: SizedHierarchicalTop => + dfs(Some(n), _getRecord(e.topGroup)) case e: PlacedHierarchicalTop => e.elements.foreach(x => dfs(Some(n), _getRecord(x))) } @@ -82,15 +116,15 @@ class FloorplanTree(val state: FloorplanState, val topMod: String) { // Option[FloorplanRecord] return // None = do no modify // Some(record) = modify node - def traverseMapPre(f: (Node => Option[FloorplanRecord])) { traverseMapPreHelper(topNode, f) } - def traverseMapPost(f: (Node => Option[FloorplanRecord])) { traverseMapPostHelper(topNode, f) } + def traverseMapPre(f: (FloorplanTreeNode => Option[FloorplanRecord])) { traverseMapPreHelper(topNode, f) } + def traverseMapPost(f: (FloorplanTreeNode => Option[FloorplanRecord])) { traverseMapPostHelper(topNode, f) } - private def traverseMapPreHelper(n: Node, f: (Node => Option[FloorplanRecord])) { + private def traverseMapPreHelper(n: FloorplanTreeNode, f: (FloorplanTreeNode => Option[FloorplanRecord])) { f(n).foreach { r => n.replace(r) } n.children.foreach { c => traverseMapPreHelper(c, f) } } - private def traverseMapPostHelper(n: Node, f: (Node => Option[FloorplanRecord])) { + private def traverseMapPostHelper(n: FloorplanTreeNode, f: (FloorplanTreeNode => Option[FloorplanRecord])) { n.children.foreach { c => traverseMapPostHelper(c, f) } f(n).foreach { r => n.replace(r) } } diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/OutOfBandAnnotationPass.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/OutOfBandAnnotationPass.scala index 1ec19698..cdd1f80c 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/OutOfBandAnnotationPass.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/OutOfBandAnnotationPass.scala @@ -39,6 +39,13 @@ class OutOfBandAnnotationPass(val sbMap: Map[String, OutOfBandAnnotation]) exten area = e.area.and(sb.areaConstraint) ) ).getOrElse(e) + case e: SizedHierarchicalTop => + sbMap.get(ofModule).map({sb => + e.copy( + width = sb.width.getOrElse(e.width), + height = sb.height.getOrElse(e.height) + ) + }).getOrElse(e) case e: PlacedHierarchicalTop => sbMap.get(ofModule).map({sb => e.copy( diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceHierarchicalPass.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceHierarchicalPass.scala index 3b84a8b7..6ae44821 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceHierarchicalPass.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceHierarchicalPass.scala @@ -13,6 +13,7 @@ class ReplaceHierarchicalPass(val topMod: String) extends Pass { // Find HierarchicalTop records, then stitch them together val topMaps = (state.records.flatMap(r => r.element match { case e: ConstrainedHierarchicalTop => Some((r.fullPath -> r)) + case e: SizedHierarchicalTop => Some((r.fullPath -> r)) case e: PlacedHierarchicalTop => Some((r.fullPath -> r)) case _ => None })).toMap @@ -78,6 +79,7 @@ class ReplaceHierarchicalPass(val topMod: String) extends Pass { area = t.area, aspectRatio = t.aspectRatio )) + case t: SizedHierarchicalTop => ??? // TODO not supported yet case t: PlacedHierarchicalTop => ??? // TODO not supported yet case _ => ??? }).map(newE => FloorplanRecord( diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/ResolveConstraintsPass.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/ResolveConstraintsPass.scala index abb1a867..d17b3341 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/ResolveConstraintsPass.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/ResolveConstraintsPass.scala @@ -38,9 +38,9 @@ class ResolveConstraintsPass(topMod: String) extends Pass { ))) case e: ConstrainedHierarchicalTop => val (width, height) = resolveConstraints(e) - Some(node.record.copy(element = PlacedHierarchicalTop( + Some(node.record.copy(element = SizedHierarchicalTop( e.name, - Seq(e.topGroup), + e.topGroup, width, height, e.margins, From cae4d928eeda3aca433330a5f0964e358fe19987 Mon Sep 17 00:00:00 2001 From: John Wright Date: Tue, 10 Aug 2021 14:50:54 -0700 Subject: [PATCH 64/73] Legalize placements (crudely) and fix HammerIR serialization of enums --- build.sbt | 1 + .../compiler/CalculatePlacementsPass.scala | 17 ++++++++++------- .../floorplan/hammer/HammerSerialization.scala | 4 ++++ 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/build.sbt b/build.sbt index 863b2ca6..272f9473 100644 --- a/build.sbt +++ b/build.sbt @@ -16,6 +16,7 @@ lazy val commonSettings = Seq( libraryDependencies ++= Seq( "org.scalatest" %% "scalatest" % "3.2.2" % "test", "org.json4s" %% "json4s-jackson" % "3.6.1", + "org.json4s" %% "json4s-ext" % "3.6.1", "org.json4s" %% "json4s-native" % "3.6.1", ), resolvers ++= Seq( diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/CalculatePlacementsPass.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/CalculatePlacementsPass.scala index d0a06aa3..3beb05f4 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/CalculatePlacementsPass.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/CalculatePlacementsPass.scala @@ -16,6 +16,9 @@ class CalculatePlacementsPass(topMod: String) extends Pass { // Custom traversal TODO should this go in the FloorplanTree code somehow? def traverse(n: FloorplanTreeNode, x: BigDecimal, y: BigDecimal, placeTopOpt: Option[FloorplanTreeNode]) { + // TODO this needs to be cleaned up elsewhere + val legalX = x.setScale(3, BigDecimal.RoundingMode.HALF_UP) + val legalY = y.setScale(3, BigDecimal.RoundingMode.HALF_UP) n.record.element match { case e: SizedHierarchicalTop => assert(placeTopOpt.isEmpty) @@ -26,8 +29,8 @@ class CalculatePlacementsPass(topMod: String) extends Pass { n.replace(n.record.copy(element = PlacedHierarchicalTop( e.name, elementBuf.toSeq.map(_.name), - x, - y, + legalX, + legalY, e.width, e.height, e.margins, @@ -43,7 +46,7 @@ class CalculatePlacementsPass(topMod: String) extends Pass { nodes.zipWithIndex.foreach { case (node, idx) => val (iX, iY) = e.fromIdx(idx) - traverse(node, x + widths(iX), y + heights(iY), placeTopOpt) + traverse(node, legalX + widths(iX), legalY + heights(iY), placeTopOpt) } // delete it @@ -53,8 +56,8 @@ class CalculatePlacementsPass(topMod: String) extends Pass { n.replace(n.record.copy(element = PlacedLogicRect( e.name, e.parent, - x, - y, + legalX, + legalY, e.width, e.height, e.hardBoundary @@ -70,8 +73,8 @@ class CalculatePlacementsPass(topMod: String) extends Pass { n.replace(n.record.copy(element = PlacedMacro( e.name, e.parent, - x, - y, + legalX, + legalY, e.width, e.height ))) diff --git a/floorplan/src/main/scala/barstools/floorplan/hammer/HammerSerialization.scala b/floorplan/src/main/scala/barstools/floorplan/hammer/HammerSerialization.scala index f3ca1847..2645bc28 100644 --- a/floorplan/src/main/scala/barstools/floorplan/hammer/HammerSerialization.scala +++ b/floorplan/src/main/scala/barstools/floorplan/hammer/HammerSerialization.scala @@ -5,6 +5,7 @@ import org.json4s._ import org.json4s.FieldSerializer.renameTo import org.json4s.native.Serialization.{read, write, writePretty} import org.json4s.JsonAST.JString +import org.json4s.ext.EnumNameSerializer object HammerSerialization { @@ -23,6 +24,9 @@ object HammerSerialization { val formats = DefaultFormats.skippingEmptyValues + + new EnumNameSerializer(PlacementType) + + new EnumNameSerializer(Orientation) + + new EnumNameSerializer(ObstructionType) + FieldSerializer[PlacementConstraint](renameTo("typ", "type")) + FieldSerializer[HammerIR](renameTo("placementConstraints", "vlsi.inputs.placement_constraints")) From 8b911d855a085312f0a2e382a9335fafc8bf88a1 Mon Sep 17 00:00:00 2001 From: John Wright Date: Tue, 10 Aug 2021 15:48:47 -0700 Subject: [PATCH 65/73] HammerIR compliance --- .../scala/barstools/floorplan/FloorplanState.scala | 2 +- .../barstools/floorplan/hammer/HammerIR.scala | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/floorplan/src/main/scala/barstools/floorplan/FloorplanState.scala b/floorplan/src/main/scala/barstools/floorplan/FloorplanState.scala index 57d639b1..b2a5bac6 100644 --- a/floorplan/src/main/scala/barstools/floorplan/FloorplanState.scala +++ b/floorplan/src/main/scala/barstools/floorplan/FloorplanState.scala @@ -5,7 +5,7 @@ import barstools.floorplan.hammer.HammerIR import java.io.{File, FileWriter} final case class FloorplanRecord(scope: String, inst: Option[String], ofModule: Option[String], element: Element) { - def fullPath = scope + inst.map(x => "/" + x).getOrElse("") + def fullPath = scope + inst.map(x => if (x.startsWith("/")) x else "/" + x).getOrElse("") } final case class FloorplanState(records: Seq[FloorplanRecord], level: Int) diff --git a/floorplan/src/main/scala/barstools/floorplan/hammer/HammerIR.scala b/floorplan/src/main/scala/barstools/floorplan/hammer/HammerIR.scala index a77c767e..b7a89ac8 100644 --- a/floorplan/src/main/scala/barstools/floorplan/hammer/HammerIR.scala +++ b/floorplan/src/main/scala/barstools/floorplan/hammer/HammerIR.scala @@ -18,7 +18,7 @@ object HammerIR { Some(PlacementConstraint( path = record.fullPath, typ = PlacementType.hardmacro, - orientation = Orientation.r0, // TODO represent this in FPIR + orientation = Some(Orientation.r0), // TODO represent this in FPIR x = toMicrons(c.x), y = toMicrons(c.y), create_physical = Some(false), @@ -34,10 +34,10 @@ object HammerIR { Some(PlacementConstraint( path = record.fullPath, typ = PlacementType.placement, - orientation = Orientation.r0, // TODO represent this in FPIR + orientation = Some(Orientation.r0), // TODO represent this in FPIR x = toMicrons(c.x), y = toMicrons(c.y), - create_physical = Some(false), + create_physical = None, width = toMicrons(c.width), height = toMicrons(c.height), master = None, @@ -49,11 +49,11 @@ object HammerIR { case c: PlacedHierarchicalTop => Some(PlacementConstraint( path = record.fullPath, - typ = PlacementType.placement, - orientation = Orientation.r0, + typ = PlacementType.toplevel, + orientation = None, x = toMicrons(c.x), y = toMicrons(c.y), - create_physical = Some(false), + create_physical = None, width = toMicrons(c.width), height = toMicrons(c.height), master = None, @@ -80,7 +80,7 @@ final case class HammerIR private[hammer] ( final case class PlacementConstraint private[hammer] ( path: String, typ: PlacementType.Value, // type is a keyword, so we use typ and rename it in the serializer - orientation: Orientation.Value, + orientation: Option[Orientation.Value], // TODO these should ideally all be decimal types, not doubles x: Double, y: Double, From 35b53805b0ced5fab42ba569d79d3d0675f06cd3 Mon Sep 17 00:00:00 2001 From: John Wright Date: Tue, 10 Aug 2021 16:36:55 -0700 Subject: [PATCH 66/73] fix bug in placement calculation --- .../compiler/CalculatePlacementsPass.scala | 4 ++-- .../floorplan/compiler/FloorplanCompiler.scala | 18 ++++++++++++++++-- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/CalculatePlacementsPass.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/CalculatePlacementsPass.scala index 3beb05f4..ab4837df 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/CalculatePlacementsPass.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/CalculatePlacementsPass.scala @@ -41,8 +41,8 @@ class CalculatePlacementsPass(topMod: String) extends Pass { // traverse down val nodes: Seq[FloorplanTreeNode] = e.elements.map(name => tree.getNode(name)) val children: Seq[SizedRectLike] = nodes.map(_.record.element.asInstanceOf[SizedRectLike]) - val widths: Seq[BigDecimal] = children.take(e.xDim).map(_.width).scanLeft(BigDecimal(0))(_+_).takeRight(e.xDim) - val heights: Seq[BigDecimal] = children.grouped(e.xDim).map(_(0).height).toSeq.scanLeft(BigDecimal(0))(_+_).takeRight(e.yDim) + val widths: Seq[BigDecimal] = children.take(e.xDim).map(_.width).scanLeft(BigDecimal(0))(_+_).take(e.xDim) + val heights: Seq[BigDecimal] = children.grouped(e.xDim).map(_(0).height).toSeq.scanLeft(BigDecimal(0))(_+_).take(e.yDim) nodes.zipWithIndex.foreach { case (node, idx) => val (iX, iY) = e.fromIdx(idx) diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanCompiler.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanCompiler.scala index 74e7893c..8d13f628 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanCompiler.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanCompiler.scala @@ -2,6 +2,7 @@ package barstools.floorplan.compiler import barstools.floorplan._ +import java.io.{File, FileWriter} case class FloorplanOptions( outFile: String = "", @@ -9,7 +10,8 @@ case class FloorplanOptions( outFmt: OutputFormat = OutputFormat.HammerIR, inFiles: Seq[String] = Seq(), sbAnnoFiles: Seq[String] = Seq(), - memInstMapFiles: Seq[String] = Seq() + memInstMapFiles: Seq[String] = Seq(), + debugFile: Option[String] = None ) object FloorplanCompiler extends App { @@ -50,13 +52,25 @@ object FloorplanCompiler extends App { action((x, c) => c.copy(outFmt = OutputFormat.FloorplanIR)). text("emit floorplanIR") + opt[String]('d', "debug-file"). + action((x, c) => c.copy(debugFile = Some(x))). + text("debug file path") + }).parse(args, FloorplanOptions()).getOrElse { throw new Exception("Error parsing options!") } // TODO make Passes customizable val fpStateIn = FloorplanState.fromFiles(opts.inFiles) - val fpStateOut = Pass.all(opts).foldLeft(fpStateIn) { (state, pass) => pass.execute(state) } + val debugWriter = opts.debugFile.map(x => new FileWriter(new File(x))) + debugWriter.foreach(_.write("Input state:\n\n")) + val fpStateOut = Pass.all(opts).foldLeft(fpStateIn) { (state, pass) => + debugWriter.foreach(_.write(FloorplanState.serialize(state))) + debugWriter.foreach(_.write("\n\nNext state:\n\n")) + pass.execute(state) + } + debugWriter.foreach(_.write(FloorplanState.serialize(fpStateOut))) + debugWriter.foreach(_.close()) FloorplanState.toFile(opts.outFile, opts.outFmt, fpStateOut) } From c980eb9143124035dfe9666b20ed2f73df5f8cd5 Mon Sep 17 00:00:00 2001 From: John Wright Date: Tue, 10 Aug 2021 17:17:21 -0700 Subject: [PATCH 67/73] Add mirroring --- .../src/main/scala/barstools/floorplan/Constraints.scala | 1 + .../barstools/floorplan/FloorplanSerialization.scala | 3 ++- floorplan/src/main/scala/barstools/floorplan/IR.scala | 6 ++++++ .../floorplan/compiler/CalculatePlacementsPass.scala | 1 + .../floorplan/compiler/OutOfBandAnnotationPass.scala | 1 + .../floorplan/compiler/ReplaceMemMacroArrayPass.scala | 8 +++++++- .../main/scala/barstools/floorplan/hammer/HammerIR.scala | 6 +----- .../barstools/floorplan/hammer/HammerSerialization.scala | 2 ++ 8 files changed, 21 insertions(+), 7 deletions(-) diff --git a/floorplan/src/main/scala/barstools/floorplan/Constraints.scala b/floorplan/src/main/scala/barstools/floorplan/Constraints.scala index 45ff9fcc..8f5c89ee 100644 --- a/floorplan/src/main/scala/barstools/floorplan/Constraints.scala +++ b/floorplan/src/main/scala/barstools/floorplan/Constraints.scala @@ -325,6 +325,7 @@ object Constraints { sealed trait PlacementAnchor +// TODO use this and convert to Enum class LowerLeft extends PlacementAnchor class LowerMiddle extends PlacementAnchor class LowerRight extends PlacementAnchor diff --git a/floorplan/src/main/scala/barstools/floorplan/FloorplanSerialization.scala b/floorplan/src/main/scala/barstools/floorplan/FloorplanSerialization.scala index 035356c6..ab97f056 100644 --- a/floorplan/src/main/scala/barstools/floorplan/FloorplanSerialization.scala +++ b/floorplan/src/main/scala/barstools/floorplan/FloorplanSerialization.scala @@ -3,6 +3,7 @@ package barstools.floorplan import org.json4s._ import org.json4s.native.Serialization.{read, write, writePretty} +import org.json4s.ext.EnumNameSerializer import scala.reflect.runtime.universe.typeOf object FloorplanSerialization { @@ -18,7 +19,7 @@ object FloorplanSerialization { val formats = (new DefaultFormats { override val typeHintFieldName = "class" override val typeHints = FullTypeHints(typeHintClasses map { x => Class.forName(x.fullName) }) - }) + } + new EnumNameSerializer(Orientation)) def serialize[T <: Element](elt: T): String = write(elt)(formats) diff --git a/floorplan/src/main/scala/barstools/floorplan/IR.scala b/floorplan/src/main/scala/barstools/floorplan/IR.scala index 77092acd..9a50b889 100644 --- a/floorplan/src/main/scala/barstools/floorplan/IR.scala +++ b/floorplan/src/main/scala/barstools/floorplan/IR.scala @@ -423,6 +423,7 @@ private[floorplan] final case class AbstractMacro ( private[floorplan] final case class SizedMacro ( name: String, parent: String, + orientation: Orientation.Value, width: BigDecimal, height: BigDecimal ) extends SizedRectPrimitive { @@ -433,6 +434,7 @@ private[floorplan] final case class SizedMacro ( private[floorplan] final case class PlacedMacro ( name: String, parent: String, + orientation: Orientation.Value, x: BigDecimal, y: BigDecimal, width: BigDecimal, @@ -441,3 +443,7 @@ private[floorplan] final case class PlacedMacro ( def mapNames(m: (String) => String): Element = this.copy(name = m(name), parent = m(parent)) } +object Orientation extends Enumeration { + val r0, r90, r180, r270, mx, mx90, my, my90 = Value +} + diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/CalculatePlacementsPass.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/CalculatePlacementsPass.scala index ab4837df..b7028d5d 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/CalculatePlacementsPass.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/CalculatePlacementsPass.scala @@ -73,6 +73,7 @@ class CalculatePlacementsPass(topMod: String) extends Pass { n.replace(n.record.copy(element = PlacedMacro( e.name, e.parent, + e.orientation, legalX, legalY, e.width, diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/OutOfBandAnnotationPass.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/OutOfBandAnnotationPass.scala index cdd1f80c..7b741137 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/OutOfBandAnnotationPass.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/OutOfBandAnnotationPass.scala @@ -59,6 +59,7 @@ class OutOfBandAnnotationPass(val sbMap: Map[String, OutOfBandAnnotation]) exten SizedMacro( name = e.name, parent = e.parent, + orientation = Orientation.r0, width = sb.width.get, height = sb.height.get ) diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceMemMacroArrayPass.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceMemMacroArrayPass.scala index c9ff1661..b80fc329 100644 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceMemMacroArrayPass.scala +++ b/floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceMemMacroArrayPass.scala @@ -71,7 +71,13 @@ class ReplaceMemMacroArrayPass(topMod: String) extends Pass { if ((group + 1) >= numMems) { addSpacer(xPadding, spacerHeight) } else { - e.elements(group + 1) + val eOut = e.elements(group + 1) + // Mirror it for proof-of-concept + val myNode = tree.getNode(eOut) + myNode.replace(myNode.record.copy(element = myNode.record.element.asInstanceOf[SizedMacro].copy( + orientation = Orientation.my + ))) + eOut } } } diff --git a/floorplan/src/main/scala/barstools/floorplan/hammer/HammerIR.scala b/floorplan/src/main/scala/barstools/floorplan/hammer/HammerIR.scala index b7a89ac8..cfabd050 100644 --- a/floorplan/src/main/scala/barstools/floorplan/hammer/HammerIR.scala +++ b/floorplan/src/main/scala/barstools/floorplan/hammer/HammerIR.scala @@ -18,7 +18,7 @@ object HammerIR { Some(PlacementConstraint( path = record.fullPath, typ = PlacementType.hardmacro, - orientation = Some(Orientation.r0), // TODO represent this in FPIR + orientation = Some(c.orientation), x = toMicrons(c.x), y = toMicrons(c.y), create_physical = Some(false), @@ -99,10 +99,6 @@ object PlacementType extends Enumeration { val dummy, placement, toplevel, hardmacro, hierarchical, obstruction = Value } -object Orientation extends Enumeration { - val r0, r90, r180, r270, mx, mx90, my, my90 = Value -} - object ObstructionType extends Enumeration { val place, route, power = Value } diff --git a/floorplan/src/main/scala/barstools/floorplan/hammer/HammerSerialization.scala b/floorplan/src/main/scala/barstools/floorplan/hammer/HammerSerialization.scala index 2645bc28..39e12d5c 100644 --- a/floorplan/src/main/scala/barstools/floorplan/hammer/HammerSerialization.scala +++ b/floorplan/src/main/scala/barstools/floorplan/hammer/HammerSerialization.scala @@ -1,6 +1,8 @@ // See LICENSE for license details package barstools.floorplan.hammer +import barstools.floorplan.Orientation + import org.json4s._ import org.json4s.FieldSerializer.renameTo import org.json4s.native.Serialization.{read, write, writePretty} From b4c1bd06d813e6c565bd9c4678f3f1c8b03a4dce Mon Sep 17 00:00:00 2001 From: Harrison Liew Date: Wed, 6 Apr 2022 20:59:20 -0700 Subject: [PATCH 68/73] match package rearrangement + chisel bump --- build.sbt | 69 +-- .../firrtl.options.RegisteredTransform | 1 + .../barstools/floorplan/Constraints.scala | 348 ++++++++++++ .../floorplan/FloorplanSerialization.scala | 39 ++ .../barstools/floorplan/FloorplanState.scala | 50 ++ src/main/scala/barstools/floorplan/IR.scala | 449 ++++++++++++++++ src/main/scala/barstools/floorplan/README | 61 +++ .../barstools/floorplan/chisel/API.scala | 494 ++++++++++++++++++ .../floorplan/chisel/FloorplanAspect.scala | 27 + .../barstools/floorplan/chisel/package.scala | 11 + .../compiler/CalculatePlacementsPass.scala | 92 ++++ .../compiler/ConstraintPropagationPass.scala | 174 ++++++ .../compiler/FloorplanCompiler.scala | 76 +++ .../floorplan/compiler/FloorplanTree.scala | 138 +++++ .../floorplan/compiler/MemInstMap.scala | 29 + .../compiler/OutOfBandAnnotation.scala | 46 ++ .../compiler/OutOfBandAnnotationPass.scala | 94 ++++ .../OutOfBandAnnotationSerialization.scala | 19 + .../barstools/floorplan/compiler/Pass.scala | 26 + .../compiler/ReplaceHierarchicalPass.scala | 102 ++++ .../compiler/ReplaceMemMacroArrayPass.scala | 110 ++++ .../compiler/ResolveConstraintsPass.scala | 90 ++++ .../compiler/TransformMemsPass.scala | 72 +++ .../floorplan/firrtl/Annotations.scala | 38 ++ .../barstools/floorplan/firrtl/Passes.scala | 114 ++++ .../barstools/floorplan/hammer/HammerIR.scala | 122 +++++ .../hammer/HammerSerialization.scala | 42 ++ .../scala/barstools/floorplan/package.scala | 18 + .../barstools/macros/MacroCompiler.scala | 42 +- .../scala/barstools/macros/SynFlopsPass.scala | 2 +- .../FloorplanReParentTransform.scala | 58 ++ .../transforms/GenerateTopAndHarness.scala | 3 +- 32 files changed, 2973 insertions(+), 83 deletions(-) create mode 100644 src/main/resources/META-INF/services/firrtl.options.RegisteredTransform create mode 100644 src/main/scala/barstools/floorplan/Constraints.scala create mode 100644 src/main/scala/barstools/floorplan/FloorplanSerialization.scala create mode 100644 src/main/scala/barstools/floorplan/FloorplanState.scala create mode 100644 src/main/scala/barstools/floorplan/IR.scala create mode 100644 src/main/scala/barstools/floorplan/README create mode 100644 src/main/scala/barstools/floorplan/chisel/API.scala create mode 100644 src/main/scala/barstools/floorplan/chisel/FloorplanAspect.scala create mode 100644 src/main/scala/barstools/floorplan/chisel/package.scala create mode 100644 src/main/scala/barstools/floorplan/compiler/CalculatePlacementsPass.scala create mode 100644 src/main/scala/barstools/floorplan/compiler/ConstraintPropagationPass.scala create mode 100644 src/main/scala/barstools/floorplan/compiler/FloorplanCompiler.scala create mode 100644 src/main/scala/barstools/floorplan/compiler/FloorplanTree.scala create mode 100644 src/main/scala/barstools/floorplan/compiler/MemInstMap.scala create mode 100644 src/main/scala/barstools/floorplan/compiler/OutOfBandAnnotation.scala create mode 100644 src/main/scala/barstools/floorplan/compiler/OutOfBandAnnotationPass.scala create mode 100644 src/main/scala/barstools/floorplan/compiler/OutOfBandAnnotationSerialization.scala create mode 100644 src/main/scala/barstools/floorplan/compiler/Pass.scala create mode 100644 src/main/scala/barstools/floorplan/compiler/ReplaceHierarchicalPass.scala create mode 100644 src/main/scala/barstools/floorplan/compiler/ReplaceMemMacroArrayPass.scala create mode 100644 src/main/scala/barstools/floorplan/compiler/ResolveConstraintsPass.scala create mode 100644 src/main/scala/barstools/floorplan/compiler/TransformMemsPass.scala create mode 100644 src/main/scala/barstools/floorplan/firrtl/Annotations.scala create mode 100644 src/main/scala/barstools/floorplan/firrtl/Passes.scala create mode 100644 src/main/scala/barstools/floorplan/hammer/HammerIR.scala create mode 100644 src/main/scala/barstools/floorplan/hammer/HammerSerialization.scala create mode 100644 src/main/scala/barstools/floorplan/package.scala create mode 100644 src/main/scala/barstools/tapeout/transforms/FloorplanReParentTransform.scala diff --git a/build.sbt b/build.sbt index 272f9473..bd57a382 100644 --- a/build.sbt +++ b/build.sbt @@ -5,47 +5,30 @@ val defaultVersions = Map( "chisel-iotesters" -> "2.5.1" ) -lazy val commonSettings = Seq( - organization := "edu.berkeley.cs", - version := "0.4-SNAPSHOT", - scalaVersion := "2.12.10", - scalacOptions := Seq("-deprecation", "-feature", "-language:reflectiveCalls", "-Xsource:2.11"), - libraryDependencies ++= Seq("chisel3","chisel-iotesters").map { - dep: String => "edu.berkeley.cs" %% dep % sys.props.getOrElse(dep + "Version", defaultVersions(dep)) - }, - libraryDependencies ++= Seq( - "org.scalatest" %% "scalatest" % "3.2.2" % "test", - "org.json4s" %% "json4s-jackson" % "3.6.1", - "org.json4s" %% "json4s-ext" % "3.6.1", - "org.json4s" %% "json4s-native" % "3.6.1", - ), - resolvers ++= Seq( - Resolver.sonatypeRepo("snapshots"), - Resolver.sonatypeRepo("releases"), - Resolver.mavenLocal - ) +organization := "edu.berkeley.cs" +version := "0.4-SNAPSHOT" +name := "tapeout" +scalaVersion := "2.12.13" +crossScalaVersions := Seq("2.12.13", "2.13.6") +scalacOptions := Seq("-deprecation", "-feature", "-language:reflectiveCalls") +Test / scalacOptions ++= Seq("-language:reflectiveCalls") +fork := true +mainClass := Some("barstools.macros.MacroCompiler") +libraryDependencies ++= Seq("chisel3","chisel-iotesters").map { + dep: String => "edu.berkeley.cs" %% dep % sys.props.getOrElse(dep + "Version", defaultVersions(dep)) +} +libraryDependencies ++= Seq( + "com.typesafe.play" %% "play-json" % "2.9.2", + "org.scalatest" %% "scalatest" % "3.2.9" % "test", + "org.apache.logging.log4j" % "log4j-api" % "2.11.2", + "org.apache.logging.log4j" % "log4j-core" % "2.11.2", + "org.json4s" %% "json4s-jackson" % "3.6.1", + "org.json4s" %% "json4s-ext" % "3.6.1", + "org.json4s" %% "json4s-native" % "3.6.1", +) +addCompilerPlugin("edu.berkeley.cs" % "chisel3-plugin" % defaultVersions("chisel3") cross CrossVersion.full) +resolvers ++= Seq( + Resolver.sonatypeRepo("snapshots"), + Resolver.sonatypeRepo("releases"), + Resolver.mavenLocal ) - -disablePlugins(sbtassembly.AssemblyPlugin) - -lazy val mdf = (project in file("mdf/scalalib")) -lazy val macros = (project in file("macros")) - .dependsOn(mdf) - .settings(commonSettings) - .settings(Seq( - libraryDependencies ++= Seq( - "edu.berkeley.cs" %% "firrtl-interpreter" % "1.2-SNAPSHOT" % Test - ), - mainClass := Some("barstools.macros.MacroCompiler") - )) - .enablePlugins(sbtassembly.AssemblyPlugin) - -lazy val floorplan = (project in file("floorplan")) - .settings(commonSettings) - -lazy val tapeout = (project in file("tapeout")) - .dependsOn(floorplan) - .settings(commonSettings) - .settings(scalacOptions in Test ++= Seq("-language:reflectiveCalls")) - -lazy val root = (project in file(".")).aggregate(macros, tapeout, floorplan) diff --git a/src/main/resources/META-INF/services/firrtl.options.RegisteredTransform b/src/main/resources/META-INF/services/firrtl.options.RegisteredTransform new file mode 100644 index 00000000..65d879eb --- /dev/null +++ b/src/main/resources/META-INF/services/firrtl.options.RegisteredTransform @@ -0,0 +1 @@ +barstools.floorplan.firrtl.GenerateFloorplanIRPass diff --git a/src/main/scala/barstools/floorplan/Constraints.scala b/src/main/scala/barstools/floorplan/Constraints.scala new file mode 100644 index 00000000..8f5c89ee --- /dev/null +++ b/src/main/scala/barstools/floorplan/Constraints.scala @@ -0,0 +1,348 @@ +// See LICENSE for license details +package barstools.floorplan + +import scala.math.{BigInt, BigDecimal, sqrt} + +sealed trait Constraint { + def and(that: Constraint): Constraint + def +(that: Constraint): Constraint + def *(that: BigDecimal): Constraint + def /(that: BigDecimal): Constraint + def test(value: BigDecimal): Boolean + def minimize: Constraint + + def resolveMin: BigDecimal = this.minimize match { + case c: Impossible => throw new Exception("Cannot reduce impossible constraint. TODO provide more detailed debug info.") + case c: Unconstrained => BigDecimal(0) + case c: Constrained => + c.eq.foreach { x => return x } + c.geq.foreach { x => + c.mof.foreach { m => + val n = (x/m).setScale(0, BigDecimal.RoundingMode.UP) + m*n + } + return x + } + BigDecimal(0) + } + + def isConstrained: Boolean +} + +final class Unconstrained extends Constraint { + def and(that: Constraint) = that + def +(that: Constraint) = that // ??? + def *(that: BigDecimal) = this + def /(that: BigDecimal) = this + def test(value: BigDecimal) = true + def minimize = this + def isConstrained = false +} + +object Unconstrained { + private val u = new Unconstrained + def apply() = u +} + +object UnconstrainedSeq { + def apply(x: Int) = Seq.fill(x) { Unconstrained() } +} + +// TODO add a reason? +final class Impossible extends Constraint { + def and(that: Constraint) = this + def +(that: Constraint) = this + def *(that: BigDecimal) = this + def /(that: BigDecimal) = this + def test(value: BigDecimal) = false + def minimize = this + def isConstrained = true +} + +object Impossible { + private val i = new Impossible + def apply() = i +} + +final case class Constrained( + eq: Option[BigDecimal] = None, + geq: Option[BigDecimal] = None, + leq: Option[BigDecimal] = None, + mof: Option[BigDecimal] = None +) extends Constraint { + + def isConstrained: Boolean = eq.isDefined || geq.isDefined || leq.isDefined || mof.isDefined + + def and(that: Constraint): Constraint = { + that match { + case that: Unconstrained => this + case that: Impossible => that + case that: Constrained => + + // Combine raw constraints + val newMof = if (this.mof.isDefined && that.mof.isDefined) { + Some(lcm(this.mof.get, that.mof.get)) + } else { + this.mof.orElse(that.mof) + } + + val newLeq = if (this.leq.isDefined && that.leq.isDefined) { + Some(Seq(this.leq.get, that.leq.get).min) + } else { + this.leq.orElse(that.leq) + } + + val newGeq = if (this.geq.isDefined && that.geq.isDefined) { + Some(Seq(this.geq.get, that.geq.get).max) + } else { + this.geq.orElse(that.geq) + } + + if (this.eq.isDefined && that.eq.isDefined && (this.eq != that.eq)) { + return Impossible() + } + val newEq = this.eq.orElse(that.eq) + + Constrained(eq=newEq, geq=newGeq, leq=newLeq, mof=newMof).minimize + case _ => ??? + } + } + + def minimize: Constraint = { + // Check range on LEQ/GEQ + val newEq = if (leq.isDefined && geq.isDefined) { + if (leq.get < geq.get) { + return Impossible() + } else if (leq.get == geq.get) { + if (eq.isDefined && (eq.get != leq.get)) { + return Impossible() + } + leq + } else { + eq + } + } else { + eq + } + + // Check multiples + if (eq.isDefined && mof.isDefined && (eq.get % mof.get != BigDecimal(0))) { + return Impossible() + } + + if (eq.isDefined) { + if (leq.isDefined) { + if (eq.get > leq.get) { + return Impossible() + } + } + if (geq.isDefined) { + if (eq.get < geq.get) { + return Impossible() + } + } + } + // TODO check if there exists a multiple in range + this.copy(eq=newEq) + } + + def +(that: Constraint): Constraint = { + that match { + case that: Unconstrained => this + case that: Impossible => that + case that: Constrained => + + // Combine raw constraints + val newMof = if (this.mof == that.mof) { + this.mof + } else { + None + } + + val newLeq = if (this.leq.isDefined && that.eq.isDefined) { + Some(this.leq.get + that.eq.get) + } else if (this.eq.isDefined && that.leq.isDefined) { + Some(this.eq.get + that.leq.get) + } else if (this.leq.isDefined && that.leq.isDefined) { + Some(this.leq.get + that.leq.get) + } else { + None + } + + val newGeq = if (this.geq.isDefined && that.eq.isDefined) { + Some(this.geq.get + that.eq.get) + } else if (this.eq.isDefined && that.geq.isDefined) { + Some(this.eq.get + that.geq.get) + } else if (this.geq.isDefined && that.geq.isDefined) { + Some(this.geq.get + that.geq.get) + } else { + None + } + + val newEq = if (this.eq.isDefined && that.eq.isDefined) { + Some(this.eq.get + that.eq.get) + } else { + None + } + + Constrained(eq=newEq, geq=newGeq, leq=newLeq, mof=newMof).minimize + case _ => ??? + } + } + + def *(that: BigDecimal): Constraint = Constrained( + eq = this.eq.map(_ * that), + geq = this.geq.map(_ * that), + leq = this.leq.map(_ * that), + mof = this.mof.map(_ * that) + ) + + def /(that: BigDecimal): Constraint = Constrained( + eq = this.eq.map(_ / that), + geq = this.geq.map(_ / that), + leq = this.leq.map(_ / that), + mof = this.mof.map(_ / that) + ) + + def test(value: BigDecimal): Boolean = { + val eqTest = this.eq.map(_ == value).getOrElse(true) + val geqTest = this.geq.map(_ <= value).getOrElse(true) + val leqTest = this.leq.map(_ >= value).getOrElse(true) + val mofTest = this.mof.map(x => (value % x) == 0).getOrElse(true) + return eqTest && geqTest && leqTest && mofTest + } +} + +object EqualTo { + def apply(value: BigDecimal) = Constrained(Some(value), None, None, None) +} + +object GreaterThanOrEqualTo { + def apply(value: BigDecimal) = Constrained(None, Some(value), None, None) +} + +object LessThanOrEqualTo { + def apply(value: BigDecimal) = Constrained(None, None, Some(value), None) +} + +object MultipleOf { + def apply(value: BigDecimal) = Constrained(None, None, None, Some(value)) +} + +case class Constraints( + width: Constraint = Unconstrained(), + height: Constraint = Unconstrained(), + area: Constraint = Unconstrained(), + aspectRatio: Constraint = Unconstrained() +) { + def test(widthValue: BigDecimal, heightValue: BigDecimal): Boolean = { + val widthTest = width.test(widthValue) + val heightTest = height.test(heightValue) + val areaTest = area.test(widthValue*heightValue) + val arTest = aspectRatio.test(heightValue/widthValue) + widthTest && heightTest && areaTest && arTest + } + + def weightXY(xWeight: BigDecimal, yWeight: BigDecimal): Constraints = Constraints( + width = this.width * xWeight, + height = this.height * yWeight, + area = this.area * (xWeight * yWeight), + aspectRatio = this.aspectRatio * (yWeight / xWeight) + ) + + def resolveMinDimensions(): (BigDecimal, BigDecimal) = resolveMinDimensions(BigDecimal(0), BigDecimal(0)) + + def resolveMinDimensions(defaultWidth: BigDecimal, defaultHeight: BigDecimal): (BigDecimal, BigDecimal) = { + if (this.aspectRatio.isConstrained) { + if (this.area.isConstrained) { + // AspectRatio with Area + if (this.width == Unconstrained() && this.height == Unconstrained()) { + val heightConstraint = BigDecimal(sqrt((this.area.resolveMin * this.aspectRatio.resolveMin).doubleValue)) // TODO clean up rounding + (this.area.resolveMin / heightConstraint, heightConstraint) + } else { + // TODO resolve 3- or 4-way constraint (this is wrong) + ??? + } + } else { + // AspectRatio with no Area + // Use defaultWidth (TODO make this an option?) + if (this.width == Unconstrained() && this.height == Unconstrained()) { + (defaultWidth, this.aspectRatio.resolveMin * defaultWidth) + } else if (this.height == Unconstrained()) { + (this.height.resolveMin / this.aspectRatio.resolveMin, this.height.resolveMin) + } else if (this.width == Unconstrained()) { + (this.width.resolveMin, this.aspectRatio.resolveMin * this.width.resolveMin) + } else { + // TODO resolve 3-way constraint + ??? + } + } + } else { + if (this.area.isConstrained) { + // Area with no AspectRatio + // Use defaultWidth (TODO make this an option?) + if (this.width == Unconstrained() && this.height == Unconstrained()) { + (defaultWidth, this.area.resolveMin / defaultWidth) + } else if (this.height == Unconstrained()) { + (this.width.resolveMin, this.area.resolveMin / this.width.resolveMin) + } else if (this.width == Unconstrained()) { + (this.area.resolveMin / this.height.resolveMin, this.height.resolveMin) + } else { + // TODO resolve 3-way constraint (this is wrong) + val widthReq = Seq(this.width.resolveMin, this.area.resolveMin / this.height.resolveMin).max + val heightReq = Seq(this.width.resolveMin, this.area.resolveMin / this.width.resolveMin).max + (widthReq, heightReq) + } + } else { + // No Area or AspectRatio + val widthConstraint = this.width match { + case x: Unconstrained => defaultWidth + case x => x.resolveMin + } + val heightConstraint = this.height match { + case x: Unconstrained => defaultHeight + case x => x.resolveMin + } + (widthConstraint, heightConstraint) + } + } + } + +} + +object Constraints { + + def sized(w: BigDecimal, h: BigDecimal): Constraints = { + Constraints( + EqualTo(w), + EqualTo(h), + Unconstrained(), + Unconstrained() + ) + } + +} + +sealed trait PlacementAnchor + +// TODO use this and convert to Enum +class LowerLeft extends PlacementAnchor +class LowerMiddle extends PlacementAnchor +class LowerRight extends PlacementAnchor +class CenterLeft extends PlacementAnchor +class CenterMiddle extends PlacementAnchor +class CenterRight extends PlacementAnchor +class UpperLeft extends PlacementAnchor +class UpperMiddle extends PlacementAnchor +class UpperRight extends PlacementAnchor + +object LowerLeft { def apply() = new LowerLeft } +object LowerMiddle { def apply() = new LowerMiddle } +object LowerRight { def apply() = new LowerRight } +object CenterLeft { def apply() = new CenterLeft } +object CenterMiddle { def apply() = new CenterMiddle } +object CenterRight { def apply() = new CenterRight } +object UpperLeft { def apply() = new UpperLeft } +object UpperMiddle { def apply() = new UpperMiddle } +object UpperRight { def apply() = new UpperRight } + diff --git a/src/main/scala/barstools/floorplan/FloorplanSerialization.scala b/src/main/scala/barstools/floorplan/FloorplanSerialization.scala new file mode 100644 index 00000000..ab97f056 --- /dev/null +++ b/src/main/scala/barstools/floorplan/FloorplanSerialization.scala @@ -0,0 +1,39 @@ +// See LICENSE for license details +package barstools.floorplan + +import org.json4s._ +import org.json4s.native.Serialization.{read, write, writePretty} +import org.json4s.ext.EnumNameSerializer +import scala.reflect.runtime.universe.typeOf + +object FloorplanSerialization { + + // Because Element is sealed, all of its subclasses are known at compile time, so we can construct type hints for them + private val typeHintClasses = Seq( + typeOf[Element], + typeOf[Unit], + typeOf[PlacementAnchor], + typeOf[Constraint], + ).map(_.typeSymbol.asClass.knownDirectSubclasses.toList).reduce(_ ++ _) + + val formats = (new DefaultFormats { + override val typeHintFieldName = "class" + override val typeHints = FullTypeHints(typeHintClasses map { x => Class.forName(x.fullName) }) + } + new EnumNameSerializer(Orientation)) + + def serialize[T <: Element](elt: T): String = write(elt)(formats) + + def deserialize(str: String): Element = { + // Not sure why the implicit doesn't work at the original definition, but the compiler complains + implicit val formats = FloorplanSerialization.formats + read[Element](str) + } + + def serializeState(state: FloorplanState): String = writePretty(state)(formats) + + def deserializeState(str: String): FloorplanState = { + implicit val formats = FloorplanSerialization.formats + read[FloorplanState](str) + } + +} diff --git a/src/main/scala/barstools/floorplan/FloorplanState.scala b/src/main/scala/barstools/floorplan/FloorplanState.scala new file mode 100644 index 00000000..b2a5bac6 --- /dev/null +++ b/src/main/scala/barstools/floorplan/FloorplanState.scala @@ -0,0 +1,50 @@ +// See LICENSE for license details +package barstools.floorplan + +import barstools.floorplan.hammer.HammerIR +import java.io.{File, FileWriter} + +final case class FloorplanRecord(scope: String, inst: Option[String], ofModule: Option[String], element: Element) { + def fullPath = scope + inst.map(x => if (x.startsWith("/")) x else "/" + x).getOrElse("") +} + +final case class FloorplanState(records: Seq[FloorplanRecord], level: Int) + +object FloorplanState { + + def fromSeq(seq: Seq[FloorplanRecord]): FloorplanState = FloorplanState(seq, (Seq(IRLevel.max) ++ seq.map(_.element.level)).max) + + def serialize(state: FloorplanState): String = FloorplanSerialization.serializeState(state) + def serialize(seq: Seq[FloorplanRecord]): String = serialize(fromSeq(seq)) + + def deserialize(str: String): FloorplanState = FloorplanSerialization.deserializeState(str) + + def fromFile(file: String): FloorplanState = { + val source = scala.io.Source.fromFile(file) + val fpState = deserialize(source.getLines.mkString("\n")) + source.close() + fpState + } + + def fromFiles(files: Seq[String]): FloorplanState = { + assert(files.length == 1, "FIXME combine multiple states into one") + fromFile(files(0)) + } + + def toFile(file: String, fmt: OutputFormat, state: FloorplanState) { + val writer = new FileWriter(new File(file)) + fmt match { + case OutputFormat.HammerIR => writer.write(HammerIR.serialize(HammerIR.fromFloorplanState(state))) + case OutputFormat.FloorplanIR => writer.write(FloorplanState.serialize(state)) + } + writer.close() + } +} + +sealed trait OutputFormat + +object OutputFormat { + case object HammerIR extends OutputFormat + case object FloorplanIR extends OutputFormat +} + diff --git a/src/main/scala/barstools/floorplan/IR.scala b/src/main/scala/barstools/floorplan/IR.scala new file mode 100644 index 00000000..9a50b889 --- /dev/null +++ b/src/main/scala/barstools/floorplan/IR.scala @@ -0,0 +1,449 @@ +// See LICENSE for license details +package barstools.floorplan + +import scala.math.{BigInt, BigDecimal} + +// TODO Make some of this stuff private +// TODO make level an enum +// TODO add versioning to the FPIR file +// TODO clean up comment syntax, add scaladoc + +////////////////////////////////////////////// Base classes + +sealed trait Element { + def name: String + def level: Int + def serialize = FloorplanSerialization.serialize(this) + + def flatIndexOf(s: String): Int + + def mapNames(m: (String) => String): Element + + def toConstraints: Constraints +} + +sealed trait Constrainable extends Element { + def applyConstraints(c: Constraints): Element +} + +sealed trait HasFixedDimensions extends Constrainable { + def width: BigDecimal + def height: BigDecimal + def area: BigDecimal = width*height + + def testConstraints(c: Constraints): Boolean = { + return c.test(width, height) + } + + final def applyConstraints(c: Constraints): Element = { + if (this.testConstraints(c)) { + this + } else { + throw new Exception("Impossible constraints applied to previously sized element") + } + } +} + +sealed trait HasPlacement extends Element { + def x: BigDecimal + def y: BigDecimal +} + +sealed abstract class ElementWithParent extends Element { + def parent: String +} + +sealed abstract class Primitive extends ElementWithParent { + def flatIndexOf(s: String): Int = -1 +} + +sealed abstract class Group extends ElementWithParent { + def elements: Seq[String] + + def flatIndexOf(s: String): Int = elements.indexOf(Some(s)) +} + +sealed abstract class Top extends Element + +////////////////////////////////////////////// Hierarchical barrier + +private[floorplan] final case class HierarchicalBarrier( + name: String, + parent: String +) extends Primitive with AbstractRectLike { + final def level = 3 + def mapNames(m: (String) => String): Element = this.copy(name = m(name), parent = m(parent)) +} + +////////////////////////////////////////////// Rectangular things + +sealed trait AbstractRectLike extends Element { + def toConstraints: Constraints = Constraints() +} + +sealed trait ConstrainedRectLike extends Constrainable { + def width: Constraint + def height: Constraint + def area: Constraint + def aspectRatio: Constraint + + def toConstraints: Constraints = Constraints(width, height, area, aspectRatio) +} + +sealed trait SizedRectLike extends HasFixedDimensions { + def toConstraints: Constraints = Constraints.sized(width, height) +} + +sealed trait PlacedRectLike extends SizedRectLike with HasPlacement + +object IRLevel { + def max = 4 +} + +sealed abstract class AbstractRectPrimitive extends Primitive with AbstractRectLike { + final def level = 4 +} + +sealed abstract class ConstrainedRectPrimitive extends Primitive with ConstrainedRectLike { + final def level = 2 +} + +sealed abstract class SizedRectPrimitive extends Primitive with SizedRectLike { + final def level = 1 +} + +sealed abstract class PlacedRectPrimitive extends Primitive with PlacedRectLike { + final def level = 0 +} + +private[floorplan] final case class ConstrainedSpacerRect( + name: String, + parent: String, + width: Constraint = Unconstrained(), + height: Constraint = Unconstrained(), + area: Constraint = Unconstrained(), + aspectRatio: Constraint = Unconstrained() +) extends ConstrainedRectPrimitive { + def mapNames(m: (String) => String): Element = this.copy(name = m(name), parent = m(parent)) + def applyConstraints(c: Constraints): Element = this.copy( + width = width.and(c.width), + height = height.and(c.height), + area = area.and(c.area), + aspectRatio = aspectRatio.and(c.aspectRatio) + ) +} + +private[floorplan] final case class SizedSpacerRect( + name: String, + parent: String, + width: BigDecimal, + height: BigDecimal +) extends SizedRectPrimitive { + def mapNames(m: (String) => String): Element = this.copy(name = m(name), parent = m(parent)) +} + +// No PlacedSpacerRect exists because they're only for spacing + +/////////////////////////// Hierarchical top modules + +case class Margins(left: BigDecimal, right: BigDecimal, top: BigDecimal, bottom: BigDecimal) + +object Margins { + def empty = Margins(BigDecimal(0), BigDecimal(0), BigDecimal(0), BigDecimal(0)) +} + +private[floorplan] final case class ConstrainedLogicRect( + name: String, + parent: String, + width: Constraint, + height: Constraint, + area: Constraint, + aspectRatio: Constraint, + hardBoundary: Boolean +) extends ConstrainedRectPrimitive { + def mapNames(m: (String) => String): Element = this.copy(name = m(name), parent = m(parent)) + def applyConstraints(c: Constraints): Element = this.copy( + width = width.and(c.width), + height = height.and(c.height), + area = area.and(c.area), + aspectRatio = aspectRatio.and(c.aspectRatio) + ) +} + +private[floorplan] final case class SizedLogicRect( + name: String, + parent: String, + width: BigDecimal, + height: BigDecimal, + hardBoundary: Boolean +) extends SizedRectPrimitive { + def mapNames(m: (String) => String): Element = this.copy(name = m(name), parent = m(parent)) +} + +private[floorplan] final case class PlacedLogicRect( + name: String, + parent: String, + x: BigDecimal, + y: BigDecimal, + width: BigDecimal, + height: BigDecimal, + hardBoundary: Boolean +) extends PlacedRectPrimitive { + def mapNames(m: (String) => String): Element = this.copy(name = m(name), parent = m(parent)) +} + + +private[floorplan] final case class ConstrainedHierarchicalTop( + name: String, + topGroup: String, + width: Constraint, + height: Constraint, + area: Constraint, + aspectRatio: Constraint, + margins: Margins, + hardBoundary: Boolean +) extends Top with ConstrainedRectLike { + final def level = 2 + def mapNames(m: (String) => String): Element = this.copy(name = m(name), topGroup = m(topGroup)) + def flatIndexOf(s: String): Int = if (topGroup == s) 0 else -1 + def applyConstraints(c: Constraints): Element = this.copy( + width = width.and(c.width), + height = height.and(c.height), + area = area.and(c.area), + aspectRatio = aspectRatio.and(c.aspectRatio) + ) +} + +private[floorplan] final case class SizedHierarchicalTop( + name: String, + topGroup: String, + width: BigDecimal, + height: BigDecimal, + margins: Margins, + hardBoundary: Boolean +) extends Top with SizedRectLike { + final def level = 1 + def mapNames(m: (String) => String): Element = this.copy(name = m(name), topGroup = m(topGroup)) + def flatIndexOf(s: String): Int = if (topGroup == s) 0 else -1 +} + +private[floorplan] final case class PlacedHierarchicalTop( + name: String, + elements: Seq[String], + x: BigDecimal, + y: BigDecimal, + width: BigDecimal, + height: BigDecimal, + margins: Margins, + hardBoundary: Boolean +) extends Top with PlacedRectLike { + final def level = 0 + def mapNames(m: (String) => String): Element = this.copy(name = m(name), elements.map(m)) + def flatIndexOf(s: String): Int = elements.indexOf(s) +} + +////////////////////////////////////////////// Aggregate (Group) things + +sealed abstract class Grid extends Group { + def xDim: Int + def yDim: Int + def elements: Seq[String] + + assert(xDim > 0, "X dimension of grid must be positive") + assert(yDim > 0, "Y dimension of grid must be positive") + assert(elements.length == (xDim*yDim), s"Grid {name} has incorrect number of elements ({elements.length})") + + def toIdx(x: Int, y: Int): Int = xDim*y + x + def fromIdx(i: Int): (Int, Int) = (i % xDim, i / xDim) + + def get(x: Int, y: Int) = elements(toIdx(x, y)) + + def indexOf(s: String): Option[(Int, Int)] = { + val idx = elements.indexOf(Some(s)) + if (idx == -1) None else Some(fromIdx(idx)) + } +} + +private[floorplan] final case class ConstrainedWeightedGrid( + name: String, + parent: String, + xDim: Int, + yDim: Int, + elements: Seq[String], + xWeights: Seq[BigDecimal], + yWeights: Seq[BigDecimal], + width: Constraint, + height: Constraint, + area: Constraint, + aspectRatio: Constraint +) extends Grid with ConstrainedRectLike { + def level = 2 + def mapNames(m: (String) => String): Element = { + this.copy( + name = m(name), + parent = m(parent), + elements = elements.map(m) + ) + } + def applyConstraints(c: Constraints): Element = this.copy( + width = width.and(c.width), + height = height.and(c.height), + area = area.and(c.area), + aspectRatio = aspectRatio.and(c.aspectRatio) + ) + + def getXWeight(x: Int): BigDecimal = xWeights(x) / xWeights.sum + def getYWeight(y: Int): BigDecimal = yWeights(y) / yWeights.sum +} + +private[floorplan] final case class ConstrainedElasticGrid( + name: String, + parent: String, + xDim: Int, + yDim: Int, + elements: Seq[String], + width: Constraint, + height: Constraint, + area: Constraint, + aspectRatio: Constraint +) extends Grid with ConstrainedRectLike { + def level = 2 + def mapNames(m: (String) => String): Element = { + this.copy( + name = m(name), + parent = m(parent), + elements = elements.map(m) + ) + } + def applyConstraints(c: Constraints): Element = this.copy( + width = width.and(c.width), + height = height.and(c.height), + area = area.and(c.area), + aspectRatio = aspectRatio.and(c.aspectRatio) + ) +} + +private[floorplan] final case class SizedGrid( + name: String, + parent: String, + xDim: Int, + yDim: Int, + elements: Seq[String], + widths: Seq[BigDecimal], + heights: Seq[BigDecimal] +) extends Grid with SizedRectLike { + def level = 1 + def mapNames(m: (String) => String): Element = { + this.copy( + name = m(name), + parent = m(parent), + elements = elements.map(m) + ) + } + def width: BigDecimal = widths.sum + def height: BigDecimal = heights.sum +} + + +////////////////////////////////////////////// MemElements and Macros + +// Reference to a MemElement +private[floorplan] final case class MemElement( + name: String, + parent: String +) extends AbstractRectPrimitive { + def mapNames(m: (String) => String): Element = this.copy(name = m(name), parent = m(parent)) +} + +// Container for MemElements +private[floorplan] final case class MemElementArray( + name: String, + parent: String, + elements: Seq[String], + width: Constraint = Unconstrained(), + height: Constraint = Unconstrained(), + area: Constraint = Unconstrained(), + aspectRatio: Constraint = Unconstrained() +) extends Group with ConstrainedRectLike { + def level = 4 + def mapNames(m: (String) => String): Element = { + this.copy( + name = m(name), + parent = m(parent), + elements = elements.map(m) + ) + } + def applyConstraints(c: Constraints): Element = this.copy( + width = width.and(c.width), + height = height.and(c.height), + area = area.and(c.area), + aspectRatio = aspectRatio.and(c.aspectRatio) + ) +} + +// Container for MemElements that have been converted to Macros +// This may be unnecessary, but the purpose of this is to let the floorplan +// tool treat memory macros differently than generic macros, since they +// are more commonly arrayed +private[floorplan] final case class MemMacroArray( + name: String, + parent: String, + elements: Seq[String], + width: Constraint = Unconstrained(), + height: Constraint = Unconstrained(), + area: Constraint = Unconstrained(), + aspectRatio: Constraint = Unconstrained() +) extends Group with ConstrainedRectLike { + def level = 2 + def mapNames(m: (String) => String): Element = { + this.copy( + name = m(name), + parent = m(parent), + elements = elements.map(m) + ) + } + def applyConstraints(c: Constraints): Element = this.copy( + width = width.and(c.width), + height = height.and(c.height), + area = area.and(c.area), + aspectRatio = aspectRatio.and(c.aspectRatio) + ) +} + +// Reference to a macro blackbox with unknown dimensions +// Do not use for SyncReadMem objects; use MemElement instead +private[floorplan] final case class AbstractMacro ( + name: String, + parent: String +) extends AbstractRectPrimitive { + def mapNames(m: (String) => String): Element = this.copy(name = m(name), parent = m(parent)) +} + +// Reference to a macro blackbox that has known dimensions +private[floorplan] final case class SizedMacro ( + name: String, + parent: String, + orientation: Orientation.Value, + width: BigDecimal, + height: BigDecimal +) extends SizedRectPrimitive { + def mapNames(m: (String) => String): Element = this.copy(name = m(name), parent = m(parent)) +} + +// Reference to a macro blackbox that has known dimensions and has been placed +private[floorplan] final case class PlacedMacro ( + name: String, + parent: String, + orientation: Orientation.Value, + x: BigDecimal, + y: BigDecimal, + width: BigDecimal, + height: BigDecimal +) extends PlacedRectPrimitive { + def mapNames(m: (String) => String): Element = this.copy(name = m(name), parent = m(parent)) +} + +object Orientation extends Enumeration { + val r0, r90, r180, r270, mx, mx90, my, my90 = Value +} + diff --git a/src/main/scala/barstools/floorplan/README b/src/main/scala/barstools/floorplan/README new file mode 100644 index 00000000..514bedef --- /dev/null +++ b/src/main/scala/barstools/floorplan/README @@ -0,0 +1,61 @@ + +Placement/Boundary constraints + + +Schema: + +- `Element` is the base abstract class + +- `Primitive` extends `Element` (abstract) + - name (String) + - target (InstanceTarget) ?? (some way of tying this to an instance) + +- `Rect` extends `Primitive` (abstract) + - x (LengthUnit) + - y (LengthUnit) + - w (LengthUnit) + - h (LengthUnit) + - rot (Rotation) + +- `Macro` extends `Rect` + +- `RouteBlockage` extends `Rect` + - layer (String) + +- `PlaceBlockage` extends `Rect` + +- `SeqMem` extends `Rect` + +- `Group` extends `Element` (abstract) + +- `Grid` extends `Group` + - elements (Array2D[Element]) + +- `HorizontalArray` extends `Array` + - elements.height = 1 + +- `VerticalArray` extends `Array` + - elements.width = 1 + +- `LayoutOptions` extends `Element` (abstract) + - layouts (List[Group]) + +- `PriorityLayoutOptions` extends `LayoutOptions` + + + +IR Level 0 +- `Rect`s must contain x, y, rot + + +IR Level 1 +- `Rect`s must contain w, h + + +IR Level 2 +- `Rect`s must contain name, target, type + + + +- Need to have an "inline" way of saying "Replace the floorplan of this block with some external hammer IR" + diff --git a/src/main/scala/barstools/floorplan/chisel/API.scala b/src/main/scala/barstools/floorplan/chisel/API.scala new file mode 100644 index 00000000..a701a73c --- /dev/null +++ b/src/main/scala/barstools/floorplan/chisel/API.scala @@ -0,0 +1,494 @@ +// See LICENSE for license details +package barstools.floorplan.chisel + +import chisel3.{RawModule, MemBase, Data} + +import firrtl.annotations.{ReferenceTarget, InstanceTarget, ModuleTarget, Target, Annotation} + +import barstools.floorplan._ +import barstools.floorplan.firrtl.{FloorplanAnnotation, MemFloorplanAnnotation, InstanceFloorplanAnnotation, NoReferenceFloorplanAnnotation} +import scala.collection.mutable.{ArraySeq, ArrayBuffer, HashMap, Set, HashSet} +import scala.math.{BigInt, BigDecimal} + +final case class ChiselFloorplanException(message: String) extends Exception(message: String) + +final class ChiselFloorplanContext private[chisel] (val scope: Target, val topElement: ChiselHierarchicalTop) { + + private[chisel] val elementBuf = new ArrayBuffer[ChiselElement]() + + elementBuf.append(topElement) + + private def addElement[T <: ChiselElement](e: T): T = { + FloorplanDatabase.register(scope, e) + elementBuf.append(e) + e + } + + def commit(): Seq[ChiselElement] = { + elementBuf.toSeq.collect { + case g: ChiselGroupElement => g.commit() + } + // Note that this is mutable and is different than above! + elementBuf.toSeq + } + + def createRect[T <: RawModule](module: T, + width: Constraint = Unconstrained(), + height: Constraint = Unconstrained(), + area: Constraint = Unconstrained(), + aspectRatio: Constraint = Unconstrained(), + hardBoundary: Boolean = true + ): ChiselLogicRect = { + val inst = module.toAbsoluteTarget.asInstanceOf[InstanceTarget] + val name = FloorplanDatabase.getUnusedName(scope, inst) + val elt = new ChiselLogicRect(scope, name, inst, width, height, area, aspectRatio, hardBoundary) + addElement(elt) + } + + def createSpacer( + name: Option[String] = None, + width: Constraint = Unconstrained(), + height: Constraint = Unconstrained(), + area: Constraint = Unconstrained(), + aspectRatio: Constraint = Unconstrained() + ): ChiselSpacerRect = { + val nameStr = FloorplanDatabase.getUnusedName(scope, name) + val elt = new ChiselSpacerRect(scope, nameStr, width, height, area, aspectRatio) + addElement(elt) + } + + def createElasticGrid(xDim: Int, yDim: Int, name: Option[String] = None): ChiselElasticGrid = { + val nameStr = FloorplanDatabase.getUnusedName(scope, name) + val elt = new ChiselElasticGrid(scope, nameStr, this, xDim, yDim) + addElement(elt) + } + + def createElasticArray(dim: Int, dir: Direction, name: Option[String]): ChiselElasticArray = { + val nameStr = FloorplanDatabase.getUnusedName(scope, name) + val elt = new ChiselElasticArray(scope, nameStr, this, dim, dir) + addElement(elt) + } + + def createElasticArray(dim: Int, name: Option[String]): ChiselElasticArray = createElasticArray(dim, Direction.Horizontal, name) + def createElasticArray(dim: Int, dir: Direction): ChiselElasticArray = createElasticArray(dim, dir, None) + def createElasticArray(dim: Int): ChiselElasticArray = createElasticArray(dim, Direction.Horizontal, None) + def createElasticArray(elts: Seq[ChiselElement], dir: Direction = Direction.Horizontal, name: Option[String] = None): ChiselElasticArray = { + val ary = createElasticArray(elts.length, dir, name) + elts.zipWithIndex.foreach { case (e, i) => ary.placeAt(i, e) } + ary + } + + def createMemArray(name: Option[String] = None, aspectRatio: Option[BigDecimal] = Some(BigDecimal(1))): ChiselMemArray = { + val nameStr = FloorplanDatabase.getUnusedName(scope, name) + val elt = new ChiselMemArray(scope, nameStr, aspectRatio, this) + addElement(elt) + } + + def addHier[T <: RawModule](module: T): ChiselHierarchicalBarrier = { + val inst = module.toAbsoluteTarget.asInstanceOf[InstanceTarget] + val name = FloorplanDatabase.getUnusedName(scope, inst) + val elt = new ChiselHierarchicalBarrier(scope, name, inst) + addElement(elt) + } + + private[chisel] def addMem[T <: Data](mem: MemBase[T]): ChiselMem = { + val ref = mem.toAbsoluteTarget + val name = FloorplanDatabase.getUnusedName(scope, ref) + val elt = new ChiselMem(scope, name, ref) + addElement(elt) + } + + def setTopGroup[T <: ChiselGroupElement](g: T): T = { + topElement.setTopGroup(g) + g + } + +} + +object Floorplan { + + + def apply[T <: RawModule](module: T): ChiselFloorplanContext = apply( + module = module, + width = Unconstrained(), + height = Unconstrained(), + area = Unconstrained(), + aspectRatio = Unconstrained(), + hardBoundary = true, + margins = Margins.empty + ) + + def apply[T <: RawModule](module: T, + width: Constraint, + height: Constraint, + area: Constraint, + aspectRatio: Constraint, + hardBoundary: Boolean, + margins: Margins + ): ChiselFloorplanContext = { + val scope: Target = module.toAbsoluteTarget + val modName = scope match { + case r: InstanceTarget => r.ofModule + case r: ModuleTarget => r.module + case _ => ??? + } + val name = FloorplanDatabase.getUnusedName(scope, modName) + val elt = new ChiselHierarchicalTop(scope, name, width, height, area, aspectRatio, margins, hardBoundary) + FloorplanDatabase.register(scope, elt) + new ChiselFloorplanContext(scope, elt) + } + + def apply[T <: RawModule](module: T, + width: Double, + height: Double + ): ChiselFloorplanContext = apply( + module = module, + width = EqualTo(BigDecimal(width)), + height = EqualTo(BigDecimal(height)), + area = Unconstrained(), + aspectRatio = Unconstrained(), + hardBoundary = true, + margins = Margins.empty + ) + + def apply[T <: RawModule](module: T, + width: String, + height: String + ): ChiselFloorplanContext = apply( + module = module, + width = EqualTo(BigDecimal(width)), + height = EqualTo(BigDecimal(height)), + area = Unconstrained(), + aspectRatio = Unconstrained(), + hardBoundary = true, + margins = Margins.empty + ) + +} + +private[chisel] object FloorplanDatabase { + + private val nameMap = new HashMap[Target, Set[String]]() + private val elements = new HashSet[ChiselElement]() + + private def getSet(scope: Target) = nameMap.getOrElseUpdate(scope, new HashSet[String]) + + // TODO I'm not sure this is necessary anymore + def register(scope: Target, element: ChiselElement): Unit = { + val name = element.name + val set = getSet(scope) + if (set.contains(name)) { + throw new ChiselFloorplanException(s"Duplicate floorplan element registration ${name} for Target "+scope.toString+"!") + } + elements.add(element) + set.add(name) + } + + def getUnusedName(scope: Target): String = getUnusedName(scope, None) + + def getUnusedName(scope: Target, suggestion: Option[String]): String = getUnusedName(scope, suggestion.getOrElse("unnamed")) + + def getUnusedName(scope: Target, suggestion: String): String = { + val set = getSet(scope) + var id = 0 + // This is slow and bad, but hopefully rare + while (set.contains(suggestion + s"_${id}")) { id = id + 1 } + suggestion + s"_${id}" + } + + def getUnusedName(scope: Target, inst: Target): String = { + val instName = inst match { + case t: InstanceTarget => t.instance + case t: ModuleTarget => t.module + case t: ReferenceTarget => t.ref + case _ => ??? + } + getUnusedName(scope, instName) + } + +} + +object FloorplanUnits { + + // TODO do we make this an annotation? + private final case class FloorplanUnitsException(message: String) extends Exception(message) + + // This corresponds to the scaling factor between user input and database units, such that the + // smallest database unit will correspond to 1. Currently only supports units <= 1. + // e.g. + // + // db unit | unit + // 0.001 um | 1000 + // 0.005 um | 200 + // 1 um | 1 + // 10 um | error + private var unit = Option.empty[Long] + + def set(x: Double) { + if (x > 1) { + throw new FloorplanUnitsException("Cannot set base unit to a value > 1.") + } + if (unit.nonEmpty) { + throw new FloorplanUnitsException("Cannot set units more than once!") + } + unit = Some(scala.math.round(1/x)) + } + + def get = unit + + def area(x: Double): BigDecimal = { + unit.map { u => + BigDecimal(scala.math.round(u*x*x)) + }.getOrElse { + throw new FloorplanUnitsException("Cannot create floorplan with concrete units- units have not been set! Call FloorplanUnits.set first.") + } + } + + def length(x: Double): BigDecimal = { + unit.map { u => + BigDecimal(scala.math.round(u*x)) + }.getOrElse { + throw new FloorplanUnitsException("Cannot create floorplan with concrete units- units have not been set! Call FloorplanUnits.set first.") + } + } + +} + +trait Direction { + def ifH[T](h: T, v: T): T = this match { + case Direction.Horizontal => h + case Direction.Vertical => v + } + def ifV[T](v: T, h: T): T = ifH(h, v) +} + +object Direction { + case object Vertical extends Direction + case object Horizontal extends Direction +} + +sealed abstract class ChiselElement(val scope: Target, val name: String) { + protected def generateElement(): Element + private[chisel] def getFloorplanAnnotations(): Seq[FloorplanAnnotation] + def getAnnotations(): Seq[Annotation] = getFloorplanAnnotations() + private var parent = Option.empty[CanBeParent] + private[chisel] def setParent(p: CanBeParent) { + assert(!this.parent.isDefined, "Element cannot have multiple parents") + this.parent = Some(p) + } + protected def parentName: String = { + assert(this.parent.isDefined, "Parent is not defined for this element") + this.parent.get.name + } +} + +sealed trait CanBeParent { + this: ChiselElement => + def name: String +} + +sealed abstract class ChiselNoReferenceElement(scope: Target, name: String) extends ChiselElement(scope, name) { + private[chisel] def getFloorplanAnnotations() = Seq(NoReferenceFloorplanAnnotation(scope, generateElement())) +} + +sealed abstract class ChiselInstanceElement(scope: Target, name: String, val instance: Target) extends ChiselElement(scope, name) { + private[chisel] def getFloorplanAnnotations() = Seq(InstanceFloorplanAnnotation(Seq(Seq(scope), Seq(instance)), generateElement())) +} + +sealed abstract class ChiselMemElement(scope: Target, name: String, val reference: ReferenceTarget) extends ChiselElement(scope, name) { + private[chisel] def getFloorplanAnnotations() = Seq(MemFloorplanAnnotation(Seq(Seq(scope), Seq(reference)), generateElement())) +} + +sealed abstract class ChiselGroupElement(scope: Target, name: String, val context: ChiselFloorplanContext) + extends ChiselElement(scope, name) with CanBeParent { + + protected def initialSize: Int + protected val elements = ArrayBuffer.fill(initialSize)(Option.empty[ChiselElement]) + protected var isCommitted = false + + protected def generateGroupElement(names: Seq[String]): Group + + private[chisel] def commit() { + // in scala 2.13 this is much cleaner: + //elements.mapInPlace(x => Some(x.getOrElse(context.createSpacer()))) + elements.zipWithIndex.foreach { case (elt, idx) => + if (elt.isEmpty) { + this._placeAt(idx, context.createSpacer()) + } + } + isCommitted = true + } + + protected def generateElement(): Group = { + assert(isCommitted) + generateGroupElement(elements.map(_.get.name)) + } + + private[chisel] def _placeAt[T <: ChiselElement](idx: Int, e: T): T = { + assert(!isCommitted, "Cannot add elements after committing") + // This is only supported in scala 2.13 + //elements.padToInPlace(idx+1, None) + for (i <- elements.length until (idx+1)) elements += None + assert(!elements(idx).isDefined, "Already added at this location") + elements(idx) = Some(e) + e.setParent(this) + e + } + + private[chisel] def getFloorplanAnnotations() = Seq(NoReferenceFloorplanAnnotation(scope, generateElement())) +} + +abstract class ChiselArrayElement(scope: Target, name: String, context: ChiselFloorplanContext) extends ChiselGroupElement(scope, name, context) { + + private[chisel] def addElement(e: ChiselElement) { + assert(!isCommitted, "Cannot add elements after committing") + elements += Some(e) + e.setParent(this) + } + +} + +abstract class ChiselGridElement(scope: Target, name: String, context: ChiselFloorplanContext, val xDim: Int, val yDim: Int) extends ChiselGroupElement(scope, name, context) { + + protected def initialSize = xDim * yDim + protected def toIdx(x: Int, y: Int): Int = xDim*y + x + + def placeAt[T <: ChiselElement](x: Int, y: Int, e: T): T = { + assert(x >= 0) + assert(y >= 0) + assert(x < xDim) + assert(y < yDim) + _placeAt(toIdx(x, y), e) + } + +} + +final class ChiselHierarchicalTop private[chisel] ( + scope: Target, + name: String, + val width: Constraint, + val height: Constraint, + val area: Constraint, + val aspectRatio: Constraint, + val margins: Margins, + val hardBoundary: Boolean +) extends ChiselNoReferenceElement(scope, name) with CanBeParent { + private var topGroup = Option.empty[ChiselGroupElement] + + private def topGroupName: String = { + assert(this.topGroup.isDefined, "HierarchicalTop needs a topGroup") + this.topGroup.get.name + } + + protected def generateElement(): Element = ConstrainedHierarchicalTop(name, topGroupName, width, height, area, aspectRatio, margins, hardBoundary) + + private[chisel] def setTopGroup(t: ChiselGroupElement) { + assert(!this.topGroup.isDefined, "Cannot set topGroup twice") + t.setParent(this) + this.topGroup = Some(t) + } +} + +final class ChiselHierarchicalBarrier private[chisel] ( + scope: Target, + name: String, + instance: InstanceTarget +) extends ChiselInstanceElement(scope, name, instance) { + protected def generateElement(): Element = HierarchicalBarrier(name, parentName) +} + +final class ChiselLogicRect private[chisel] ( + scope: Target, + name: String, + instance: InstanceTarget, + val width: Constraint, + val height: Constraint, + val area: Constraint, + val aspectRatio: Constraint, + val hardBoundary: Boolean +) extends ChiselInstanceElement(scope, name, instance) { + protected def generateElement(): Element = ConstrainedLogicRect(name, parentName, width, height, area, aspectRatio, hardBoundary) +} + +final class ChiselSpacerRect private[chisel] ( + scope: Target, + name: String, + val width: Constraint = Unconstrained(), + val height: Constraint = Unconstrained(), + val area: Constraint = Unconstrained(), + val aspectRatio: Constraint = Unconstrained() +) extends ChiselNoReferenceElement(scope, name) { + protected def generateElement(): Element = ConstrainedSpacerRect(name, parentName, width, height, area, aspectRatio) +} + +final class ChiselMem private[chisel] ( + scope: Target, + name: String, + reference: ReferenceTarget +) extends ChiselMemElement(scope, name, reference) { + protected def generateElement(): Element = MemElement(name, parentName) +} + +final class ChiselMemArray private[chisel] ( + scope: Target, + name: String, + aspectRatio: Option[BigDecimal], + context: ChiselFloorplanContext +) extends ChiselArrayElement(scope, name, context) { + protected def initialSize = 0 + protected def generateGroupElement(names: Seq[String]): Group = MemElementArray(name, parentName, names, Unconstrained(), Unconstrained(), Unconstrained(), Constrained(eq = aspectRatio)) + + def addMem[T <: Data](mem: MemBase[T]) = this.addElement(this.context.addMem(mem)) +} + +class ChiselElasticGrid private[chisel] ( + scope: Target, + name: String, + context: ChiselFloorplanContext, + xDim: Int, + yDim: Int +) extends ChiselGridElement(scope, name, context, xDim, yDim) { + final protected def generateGroupElement(names: Seq[String]): Group = ConstrainedElasticGrid( + name, + parentName, + xDim, + yDim, + names, + Unconstrained(), + Unconstrained(), + Unconstrained(), + Unconstrained()) +} + +class ChiselElasticArray private[chisel] ( + scope: Target, + name: String, + context: ChiselFloorplanContext, + dim: Int, + val dir: Direction +) extends ChiselElasticGrid(scope, name, context, dir.ifH(dim,1), dir.ifV(dim,1)) { + def placeAt[T <: ChiselElement](i: Int, e: T): T = super.placeAt(dir.ifH(i,0), dir.ifV(i,0), e) +} + +class ChiselWeightedGrid private[chisel] ( + scope: Target, + name: String, + context: ChiselFloorplanContext, + xDim: Int, + yDim: Int, + xWeights: Seq[BigDecimal], + yWeights: Seq[BigDecimal] +) extends ChiselGridElement(scope, name, context, xDim, yDim) { + final protected def generateGroupElement(names: Seq[String]): Group = ConstrainedWeightedGrid( + name, + parentName, + xDim, + yDim, + names, + xWeights, + yWeights, + Unconstrained(), + Unconstrained(), + Unconstrained(), + Unconstrained()) +} diff --git a/src/main/scala/barstools/floorplan/chisel/FloorplanAspect.scala b/src/main/scala/barstools/floorplan/chisel/FloorplanAspect.scala new file mode 100644 index 00000000..60952d4a --- /dev/null +++ b/src/main/scala/barstools/floorplan/chisel/FloorplanAspect.scala @@ -0,0 +1,27 @@ +// See LICENSE for license details +package barstools.floorplan.chisel + +import chisel3.{RawModule} +import chisel3.aop.{Aspect, Select} +import firrtl.{AnnotationSeq} + +abstract class FloorplanAspect[T <: RawModule](floorplans: FloorplanFunction) extends Aspect[T] { + + // Guarantee only one FloorplanAspect will be created + FloorplanAspect.setHasRun() + + final override def toAnnotation(top: T): AnnotationSeq = { + AnnotationSeq(Select.collectDeep(top)(floorplans).toList.flatten.flatMap(_.getAnnotations())) + } +} + +object FloorplanAspect { + private var hasRun = false + + private[chisel] def setHasRun() = { + if (hasRun) { + throw new Exception("Cannot run FloorplanAspect more than once; combine partial functions into a single FloorplanAspect instead.") + } + hasRun = true + } +} diff --git a/src/main/scala/barstools/floorplan/chisel/package.scala b/src/main/scala/barstools/floorplan/chisel/package.scala new file mode 100644 index 00000000..6415cc0c --- /dev/null +++ b/src/main/scala/barstools/floorplan/chisel/package.scala @@ -0,0 +1,11 @@ +// See LICENSE for license details +package barstools.floorplan + +package object chisel { + + import chisel3.experimental.{BaseModule} + + type FloorplanFunction = PartialFunction[BaseModule, Seq[ChiselElement]] + +} + diff --git a/src/main/scala/barstools/floorplan/compiler/CalculatePlacementsPass.scala b/src/main/scala/barstools/floorplan/compiler/CalculatePlacementsPass.scala new file mode 100644 index 00000000..b7028d5d --- /dev/null +++ b/src/main/scala/barstools/floorplan/compiler/CalculatePlacementsPass.scala @@ -0,0 +1,92 @@ +// See LICENSE for license details +package barstools.floorplan.compiler + +import barstools.floorplan._ + +import scala.collection.mutable.{ArrayBuffer} + +class CalculatePlacementsPass(topMod: String) extends Pass { + def execute(state: FloorplanState): FloorplanState = { + val tree = new FloorplanTree(state, topMod) + + val top = tree.topNode + assert(top.record.element.isInstanceOf[SizedHierarchicalTop], "PlacedHierarchicalTop not yet supported pre-CalculatePlacementsPass. Use SizedHierarchicalTop instead.") + + val elementBuf = new ArrayBuffer[Element]() + + // Custom traversal TODO should this go in the FloorplanTree code somehow? + def traverse(n: FloorplanTreeNode, x: BigDecimal, y: BigDecimal, placeTopOpt: Option[FloorplanTreeNode]) { + // TODO this needs to be cleaned up elsewhere + val legalX = x.setScale(3, BigDecimal.RoundingMode.HALF_UP) + val legalY = y.setScale(3, BigDecimal.RoundingMode.HALF_UP) + n.record.element match { + case e: SizedHierarchicalTop => + assert(placeTopOpt.isEmpty) + // Note: we intentionally wait to add this until the end so we know all of the elements + val children = n.children + assert(children.length == 1) // should only be a topGroup here + children.foreach { cNode => traverse(cNode, x, y, Some(n)) } + n.replace(n.record.copy(element = PlacedHierarchicalTop( + e.name, + elementBuf.toSeq.map(_.name), + legalX, + legalY, + e.width, + e.height, + e.margins, + e.hardBoundary + ))) + case e: SizedGrid => + assert(placeTopOpt.isDefined) + // traverse down + val nodes: Seq[FloorplanTreeNode] = e.elements.map(name => tree.getNode(name)) + val children: Seq[SizedRectLike] = nodes.map(_.record.element.asInstanceOf[SizedRectLike]) + val widths: Seq[BigDecimal] = children.take(e.xDim).map(_.width).scanLeft(BigDecimal(0))(_+_).take(e.xDim) + val heights: Seq[BigDecimal] = children.grouped(e.xDim).map(_(0).height).toSeq.scanLeft(BigDecimal(0))(_+_).take(e.yDim) + + nodes.zipWithIndex.foreach { case (node, idx) => + val (iX, iY) = e.fromIdx(idx) + traverse(node, legalX + widths(iX), legalY + heights(iY), placeTopOpt) + } + + // delete it + n.delete() + case e: SizedLogicRect => + assert(placeTopOpt.isDefined) + n.replace(n.record.copy(element = PlacedLogicRect( + e.name, + e.parent, + legalX, + legalY, + e.width, + e.height, + e.hardBoundary + ))) + n.reparent(placeTopOpt.get) + elementBuf.append(n.record.element) + case e: SizedSpacerRect => + assert(placeTopOpt.isDefined) + // delete it + n.delete() + case e: SizedMacro => + assert(placeTopOpt.isDefined) + n.replace(n.record.copy(element = PlacedMacro( + e.name, + e.parent, + e.orientation, + legalX, + legalY, + e.width, + e.height + ))) + n.reparent(placeTopOpt.get) + elementBuf.append(n.record.element) + case e => ??? // Shouldn't get here + } + } + + traverse(top, BigDecimal(0), BigDecimal(0), None) + + tree.toState + } +} diff --git a/src/main/scala/barstools/floorplan/compiler/ConstraintPropagationPass.scala b/src/main/scala/barstools/floorplan/compiler/ConstraintPropagationPass.scala new file mode 100644 index 00000000..d5fc62c2 --- /dev/null +++ b/src/main/scala/barstools/floorplan/compiler/ConstraintPropagationPass.scala @@ -0,0 +1,174 @@ +// See LICENSE for license details +package barstools.floorplan.compiler + +import barstools.floorplan._ + +class ConstraintPropagationPass(val topMod: String) extends Pass { + def execute(state: FloorplanState): FloorplanState = { + val tree = new FloorplanTree(state, topMod) + + // Iterate a couple times (this is a hack FIXME) + val nIter = 3 + for (iter <- (0 until nIter)) { + // Top-down pass + tree.traverseMapPre { node => + val constraints: Constraints = node.parent.map(_.record.element match { + case e: ConstrainedHierarchicalTop => + e.toConstraints + case e: SizedHierarchicalTop => + Constraints() // These should be sized already + case e: PlacedHierarchicalTop => + Constraints() // These should be sized already + case e: ConstrainedWeightedGrid => + val (x, y) = e.indexOf(node.record.element.name).get + val xWeight = e.getXWeight(x) + val yWeight = e.getYWeight(y) + e.toConstraints.weightXY(xWeight, yWeight) + case e: ConstrainedElasticGrid => + val c = e.toConstraints + val widthConstraint = c.width match { + case x: Unconstrained => x + case x: Impossible => x + case x: Constrained => x.copy(eq = None, leq = x.eq.orElse(x.leq), geq = None, mof = None) + } + val heightConstraint = c.height match { + case x: Unconstrained => x + case x: Impossible => x + case x: Constrained => x.copy(eq = None, leq = x.eq.orElse(x.leq), geq = None, mof = None) + } + val areaConstraint = c.area match { + case x: Unconstrained => x + case x: Impossible => x + case x: Constrained => x.copy(eq = None, leq = x.eq.orElse(x.leq), geq = None, mof = None) + } + Constraints(width = widthConstraint, height = heightConstraint, area = areaConstraint, aspectRatio = Unconstrained()) + case e: SizedGrid => + val (x, y) = e.indexOf(node.record.element.name).get + Constraints.sized(e.widths(x), e.heights(y)) + case e: MemMacroArray => + Constraints() // These *should* be hard macros at this point, so no need to constrain them. Maybe aspect ratio? + case _ => ??? // Many types can never be parents and shouldn't get here + }).getOrElse(Constraints()) + + // Only modify child + val newElement = node.record.element match { + case e: Constrainable => + e.applyConstraints(constraints) + case e => ??? + } + Some(node.record.copy(element = newElement)) + } + + + // Bottom-up pass + tree.traverseMapPost { node => + node.record.element match { + case e: ConstrainedSpacerRect => + None + case e: SizedSpacerRect => + None + case e: ConstrainedLogicRect => + None + case e: SizedLogicRect => + None + case e: PlacedLogicRect => + None + case e: ConstrainedHierarchicalTop => + val newElement = e.applyConstraints(node.children(0).record.element.toConstraints) + Some(node.record.copy(element = newElement)) + case e: SizedHierarchicalTop => + throw new Exception("Cannot propagate constraints to a SizedHierarchicalTop") + case e: PlacedHierarchicalTop => + throw new Exception("Cannot propagate constraints to a PlacedHierarchicalTop") + case e: ConstrainedWeightedGrid => + // TODO make this less repetitive with the below + // Preserve ordering of children and convert to 2d Seq + val children: Seq[Seq[Constraints]] = e.elements.map(x => tree.getRecord(x).element.toConstraints).grouped(e.xDim).toSeq + val widthConstraint = children.map(_.map(_.width).reduce(_ + _)).reduce(_ and _) + val heightConstraint = children.transpose.map(_.map(_.height).reduce(_ + _)).reduce(_ and _) + val areaConstraint = children.flatten.map(_.area).reduce(_ + _) + val newElement = e.applyConstraints(Constraints( + widthConstraint, + heightConstraint, + areaConstraint, + Unconstrained() + )) + // TODO handle the weights + Some(node.record.copy(element = newElement)) + case e: ConstrainedElasticGrid => + // Preserve ordering of children and convert to 2d Seq + val children: Seq[Seq[Constraints]] = e.elements.map(x => tree.getRecord(x).element.toConstraints).grouped(e.xDim).toSeq + val widthConstraint = children.map(_.map(_.width).reduce(_ + _)).reduce(_ and _) + val heightConstraint = children.transpose.map(_.map(_.height).reduce(_ + _)).reduce(_ and _) + val areaConstraint = children.flatten.map(_.area).reduce(_ + _) + val newElement = e.applyConstraints(Constraints( + widthConstraint, + heightConstraint, + areaConstraint, + Unconstrained() + )) + Some(node.record.copy(element = newElement)) + case e: SizedGrid => + ??? // TODO probably should sanity check here + case e: MemMacroArray => + val area = node.children.map(_.record.element match { + case e2: HasFixedDimensions => e2.area + case e2 => throw new Exception("All macro dimensions must be resolved at this point") + }).sum + val newElement = e.applyConstraints(Constraints( + Unconstrained(), + Unconstrained(), + GreaterThanOrEqualTo(area), + Unconstrained() + )) + Some(node.record.copy(element = newElement)) + case e: SizedMacro => + None + case e: PlacedMacro => + None + case _ => ??? + } + } + + // Sibling propagation to Grids + tree.allNodes.values.foreach { node => + node.record.element match { + case e: SizedGrid => + // Do nothing + case e: Grid => + // This look-up preserves ordering of children + val children = e.elements.map(x => tree.getNode(tree.getRecord(x).element.name)) + val wConstraints = Seq.tabulate(e.xDim)(iX => Seq.tabulate(e.yDim) { iY => + children(e.toIdx(iX, iY)).record.element.toConstraints.width + } reduce (_ and _)) + val hConstraints = Seq.tabulate(e.yDim)(iY => Seq.tabulate(e.xDim) { iX => + children(e.toIdx(iX, iY)).record.element.toConstraints.height + } reduce (_ and _)) + val newElements = children.zipWithIndex.map { case (child, idx) => + val (iX, iY) = e.fromIdx(idx) + // We can always assume this is Constrainable- but really we should fix this in the type system + child.replace(child.record.copy( + element = child.record.element.asInstanceOf[Constrainable]. + applyConstraints(Constraints(width = wConstraints(iX), height = hConstraints(iY), Unconstrained(), Unconstrained())) + )) + child.record.element.name + } + // Needed to be able to use copy + e match { + case e: ConstrainedWeightedGrid => + node.replace(node.record.copy(element = e.copy(elements = newElements))) + case e: ConstrainedElasticGrid => + node.replace(node.record.copy(element = e.copy(elements = newElements))) + case e => + ??? + } + case e => + // Do nothing + } + } + + } // End of iteration loop + + tree.toState + } +} diff --git a/src/main/scala/barstools/floorplan/compiler/FloorplanCompiler.scala b/src/main/scala/barstools/floorplan/compiler/FloorplanCompiler.scala new file mode 100644 index 00000000..8d13f628 --- /dev/null +++ b/src/main/scala/barstools/floorplan/compiler/FloorplanCompiler.scala @@ -0,0 +1,76 @@ +// See LICENSE for license details +package barstools.floorplan.compiler + +import barstools.floorplan._ +import java.io.{File, FileWriter} + +case class FloorplanOptions( + outFile: String = "", + topMod: String = "", + outFmt: OutputFormat = OutputFormat.HammerIR, + inFiles: Seq[String] = Seq(), + sbAnnoFiles: Seq[String] = Seq(), + memInstMapFiles: Seq[String] = Seq(), + debugFile: Option[String] = None +) + +object FloorplanCompiler extends App { + + val opts = (new scopt.OptionParser[FloorplanOptions]("fpCompiler") { + + opt[String]('i', "input-file"). + required(). + valueName(""). + action((x, c) => c.copy(inFiles = c.inFiles :+ x)). + text("input file name") + + opt[String]('t', "top-module"). + required(). + valueName(""). + action((x, c) => c.copy(topMod = x)). + text("top module name") + + opt[String]('o', "output-file"). + required(). + valueName(""). + action((x, c) => c.copy(outFile = x)). + text("output file name") + + opt[String]('m', "mem-inst-file"). + required(). + valueName(""). + action((x, c) => c.copy(memInstMapFiles = c.memInstMapFiles :+ x)). + text("file containing the memory instance map") + + opt[String]('b', "oob-anno-file"). + required(). + valueName(""). + action((x, c) => c.copy(sbAnnoFiles = c.sbAnnoFiles :+ x)). + text("output file name") + + opt[Unit]('f', "output-fpir"). + action((x, c) => c.copy(outFmt = OutputFormat.FloorplanIR)). + text("emit floorplanIR") + + opt[String]('d', "debug-file"). + action((x, c) => c.copy(debugFile = Some(x))). + text("debug file path") + + }).parse(args, FloorplanOptions()).getOrElse { + throw new Exception("Error parsing options!") + } + + // TODO make Passes customizable + val fpStateIn = FloorplanState.fromFiles(opts.inFiles) + val debugWriter = opts.debugFile.map(x => new FileWriter(new File(x))) + debugWriter.foreach(_.write("Input state:\n\n")) + val fpStateOut = Pass.all(opts).foldLeft(fpStateIn) { (state, pass) => + debugWriter.foreach(_.write(FloorplanState.serialize(state))) + debugWriter.foreach(_.write("\n\nNext state:\n\n")) + pass.execute(state) + } + debugWriter.foreach(_.write(FloorplanState.serialize(fpStateOut))) + debugWriter.foreach(_.close()) + FloorplanState.toFile(opts.outFile, opts.outFmt, fpStateOut) + +} diff --git a/src/main/scala/barstools/floorplan/compiler/FloorplanTree.scala b/src/main/scala/barstools/floorplan/compiler/FloorplanTree.scala new file mode 100644 index 00000000..76da7dc0 --- /dev/null +++ b/src/main/scala/barstools/floorplan/compiler/FloorplanTree.scala @@ -0,0 +1,138 @@ +// See LICENSE for license details +package barstools.floorplan.compiler + +import scala.collection.mutable.{ArrayBuffer, HashMap} +import barstools.floorplan._ + +// TODO this is required to give access to Node types outside FloorplanTree. Ideally we should move all that functionality into this class +// with some type-generic methods, but for now this works +sealed trait FloorplanTreeNode { + + def parent: Option[FloorplanTreeNode] + def children: Seq[FloorplanTreeNode] + def record: FloorplanRecord + def addChildRecord(cRecord: FloorplanRecord): FloorplanTreeNode + def removeChild(n: FloorplanTreeNode): Unit + def delete(): Unit + def replace(r: FloorplanRecord): Unit + def reparent(p: FloorplanTreeNode): Unit + +} + +class FloorplanTree(val state: FloorplanState, val topMod: String) { + + val allNodes = new HashMap[String, FloorplanTreeNode]() + + class Node(val parent: Option[FloorplanTreeNode], initialRecord: FloorplanRecord) extends FloorplanTreeNode { + val _children = new ArrayBuffer[FloorplanTreeNode]() + + // TODO this might be dangerous + private var _record = initialRecord + + def children = _children.toSeq + def record = _record + + def addChildRecord(cRecord: FloorplanRecord): FloorplanTreeNode = { + val n = new Node(Some(this), cRecord) + _children += n + allNodes += (cRecord.element.name -> n) + n + } + + def removeChild(n: FloorplanTreeNode) { + _children.remove(_children.indexOf(n)) + } + + def delete() { + assert(_children.isEmpty) // sanity check that we aren't orphaning nodes + parent.foreach(_.removeChild(this)) + allNodes.remove(record.element.name) + } + + def replace(r: FloorplanRecord) { _record = r } + + def reparent(p: FloorplanTreeNode) { + this.delete() + p.addChildRecord(record) + } + } + + + def getUniqueName(suggestion: String): String = { + var i = 0 + var tmp = suggestion + s"_${i}" + while (allNodes.keys.toSet.contains(tmp)) { + i = i + 1 + tmp = suggestion + s"_${i}" + } + tmp + } + + def getRecord(s: String): FloorplanRecord = getNode(s).record + def getNode(s: String): FloorplanTreeNode = allNodes(s) + + // These are only used by the constructor + private val allRecords: Map[String, FloorplanRecord] = state.records.map({ x => (x.element.name -> x) }).toMap + private def _getRecord(s: String): FloorplanRecord = allRecords(s) + + val topRecords = state.records.flatMap({ r => r.element match { + case e: Top => Seq(r) + case _ => Seq() + }}) + assert(topRecords.length == 1, "Must be exactly one Top record") + val topRecord = topRecords(0) + + private def dfs(parent: Option[FloorplanTreeNode], r: FloorplanRecord): FloorplanTreeNode = { + r.element match { + case e: Top => + assert(!parent.isDefined, "Cannot have multiple tops") + val n = new Node(None, r) + // There's probably a better way to handle these + e match { + case e: ConstrainedHierarchicalTop => + dfs(Some(n), _getRecord(e.topGroup)) + case e: SizedHierarchicalTop => + dfs(Some(n), _getRecord(e.topGroup)) + case e: PlacedHierarchicalTop => + e.elements.foreach(x => dfs(Some(n), _getRecord(x))) + } + allNodes += (e.name -> n) + n + case e: Primitive => + assert(parent.isDefined, "Must have parent") + parent.get.addChildRecord(r) + case e: Group => + assert(parent.isDefined, "Must have parent") + val n = parent.get.addChildRecord(r) + e.elements.foreach(x => dfs(Some(n), _getRecord(x))) + n + case _ => ??? + } + } + + val topNode = dfs(None, topRecord) + + // Traverse using DFS, passing the node to a function which expects an + // Option[FloorplanRecord] return + // None = do no modify + // Some(record) = modify node + def traverseMapPre(f: (FloorplanTreeNode => Option[FloorplanRecord])) { traverseMapPreHelper(topNode, f) } + def traverseMapPost(f: (FloorplanTreeNode => Option[FloorplanRecord])) { traverseMapPostHelper(topNode, f) } + + private def traverseMapPreHelper(n: FloorplanTreeNode, f: (FloorplanTreeNode => Option[FloorplanRecord])) { + f(n).foreach { r => n.replace(r) } + n.children.foreach { c => traverseMapPreHelper(c, f) } + } + + private def traverseMapPostHelper(n: FloorplanTreeNode, f: (FloorplanTreeNode => Option[FloorplanRecord])) { + n.children.foreach { c => traverseMapPostHelper(c, f) } + f(n).foreach { r => n.replace(r) } + } + + def toState: FloorplanState = { + val records = allNodes.values.map(_.record).toSeq + val level = records.map(_.element.level).max + FloorplanState(records, level) + } + +} diff --git a/src/main/scala/barstools/floorplan/compiler/MemInstMap.scala b/src/main/scala/barstools/floorplan/compiler/MemInstMap.scala new file mode 100644 index 00000000..949c070b --- /dev/null +++ b/src/main/scala/barstools/floorplan/compiler/MemInstMap.scala @@ -0,0 +1,29 @@ +// See LICENSE for license details +package barstools.floorplan.compiler + +import scala.collection.Map +import scala.collection.mutable.HashMap +import java.io.File +import scala.io.Source + +import firrtl.annotations.TargetToken.{Instance, OfModule} + +object MemInstMap { + + def fromFiles(files: Seq[String]): Map[OfModule, Seq[(Instance, OfModule)]] = { + val hashMap = new HashMap[OfModule, Seq[(Instance, OfModule)]]() + files.foreach { f => + Source.fromFile(new File(f)).getLines.foreach { line: String => + val listRaw = line.split(" ") + val module = OfModule(listRaw(0)) + hashMap += module -> listRaw.toSeq.drop(1).map { x => + val s = x.split(":") + (Instance(s(0)), OfModule(s(1))) + } + } + } + hashMap.toMap + } + +} + diff --git a/src/main/scala/barstools/floorplan/compiler/OutOfBandAnnotation.scala b/src/main/scala/barstools/floorplan/compiler/OutOfBandAnnotation.scala new file mode 100644 index 00000000..6e8e6eb2 --- /dev/null +++ b/src/main/scala/barstools/floorplan/compiler/OutOfBandAnnotation.scala @@ -0,0 +1,46 @@ +// See LICENSE for license details +package barstools.floorplan.compiler + +import barstools.floorplan._ +import scala.collection.{Seq, Map} + +case class OutOfBandAnnotation( + ofModule: String, + width: Option[BigDecimal] = None, + height: Option[BigDecimal] = None, + area: Option[BigDecimal] = None +) { + def ++(that: OutOfBandAnnotation): OutOfBandAnnotation = { + assert(this.ofModule == that.ofModule) + assert(!this.width.isDefined || !that.width.isDefined) + assert(!this.height.isDefined || !that.height.isDefined) + assert(!this.area.isDefined || !that.area.isDefined) + OutOfBandAnnotation( + this.ofModule, + this.width.orElse(that.width), + this.height.orElse(that.width), + this.area.orElse(that.area) + ) + } + + def widthConstraint = width.map(x => EqualTo(x)).getOrElse(Unconstrained()) + def heightConstraint = height.map(x => EqualTo(x)).getOrElse(Unconstrained()) + def areaConstraint = area.map(x => EqualTo(x)).getOrElse(Unconstrained()) +} + +object OutOfBandAnnotationMap { + def fromFiles(files: Seq[String]): Map[String, OutOfBandAnnotation] = fromSeq(OutOfBandAnnotationSeq.fromFiles(files)) + def fromFile(file: String): Map[String, OutOfBandAnnotation] = fromSeq(OutOfBandAnnotationSeq.fromFile(file)) + def fromSeq(seq: Seq[OutOfBandAnnotation]): Map[String, OutOfBandAnnotation] = seq.groupBy(_.ofModule).mapValues(_.reduce(_ ++ _)) +} + +object OutOfBandAnnotationSeq { + def fromFiles(files: Seq[String]): Seq[OutOfBandAnnotation] = files.flatMap(x => fromFile(x)) + def fromFile(file: String): Seq[OutOfBandAnnotation] = { + val source = scala.io.Source.fromFile(file) + val annos = OutOfBandAnnotationSerialization.deserialize(source.getLines.mkString("\n")) + source.close() + annos + } +} + diff --git a/src/main/scala/barstools/floorplan/compiler/OutOfBandAnnotationPass.scala b/src/main/scala/barstools/floorplan/compiler/OutOfBandAnnotationPass.scala new file mode 100644 index 00000000..7b741137 --- /dev/null +++ b/src/main/scala/barstools/floorplan/compiler/OutOfBandAnnotationPass.scala @@ -0,0 +1,94 @@ +// See LICENSE for license details +package barstools.floorplan.compiler + +import barstools.floorplan._ +import scala.collection.Map + +class OutOfBandAnnotationPass(val sbMap: Map[String, OutOfBandAnnotation]) extends Pass { + def execute(state: FloorplanState): FloorplanState = { + val newRecords = state.records.map { record => + val ofModule = record.ofModule.getOrElse("") + val newElement = record.element match { + case e: ConstrainedLogicRect => + sbMap.get(ofModule).map(sb => + e.copy( + width = e.width.and(sb.widthConstraint), + height = e.height.and(sb.heightConstraint), + area = e.area.and(sb.areaConstraint) + ) + ).getOrElse(e) + case e: SizedLogicRect => + sbMap.get(ofModule).map(sb => + e.copy( + width = sb.width.getOrElse(e.width), + height = sb.height.getOrElse(e.height) + ) + ).getOrElse(e) + case e: PlacedLogicRect => + sbMap.get(ofModule).map(sb => + e.copy( + width = sb.width.getOrElse(e.width), + height = sb.height.getOrElse(e.height) + ) + ).getOrElse(e) + case e: ConstrainedHierarchicalTop => + sbMap.get(ofModule).map(sb => + e.copy( + width = e.width.and(sb.widthConstraint), + height = e.height.and(sb.heightConstraint), + area = e.area.and(sb.areaConstraint) + ) + ).getOrElse(e) + case e: SizedHierarchicalTop => + sbMap.get(ofModule).map({sb => + e.copy( + width = sb.width.getOrElse(e.width), + height = sb.height.getOrElse(e.height) + ) + }).getOrElse(e) + case e: PlacedHierarchicalTop => + sbMap.get(ofModule).map({sb => + e.copy( + width = sb.width.getOrElse(e.width), + height = sb.height.getOrElse(e.height) + ) + }).getOrElse(e) + case e: AbstractMacro => + sbMap.get(ofModule).map({sb => + assert(sb.width.isDefined && sb.height.isDefined, "Macro out-of-band annotations must include width and height") + SizedMacro( + name = e.name, + parent = e.parent, + orientation = Orientation.r0, + width = sb.width.get, + height = sb.height.get + ) + }).getOrElse(e) + case e: SizedMacro => + sbMap.get(ofModule).map({sb => + assert(sb.width.isDefined && sb.height.isDefined, "Macro out-of-band annotations must include width and height") + e.copy( + width = sb.width.get, + height = sb.height.get + ) + }).getOrElse(e) + case e: PlacedMacro => + sbMap.get(ofModule).map({sb => + assert(sb.width.isDefined && sb.height.isDefined, "Macro out-of-band annotations must include width and height") + e.copy( + width = sb.width.get, + height = sb.height.get + ) + }).getOrElse(e) + case e => e + } + record.copy(element = newElement) + } + newRecords.map(_.element).collectFirst { + case e: AbstractMacro => + throw new Exception("Unannotated macros still exist after OutOfBandAnnotationPass") + } + state.copy(records = newRecords) + } +} + diff --git a/src/main/scala/barstools/floorplan/compiler/OutOfBandAnnotationSerialization.scala b/src/main/scala/barstools/floorplan/compiler/OutOfBandAnnotationSerialization.scala new file mode 100644 index 00000000..145033b6 --- /dev/null +++ b/src/main/scala/barstools/floorplan/compiler/OutOfBandAnnotationSerialization.scala @@ -0,0 +1,19 @@ +// See LICENSE for license details +package barstools.floorplan.compiler + +import org.json4s._ +import org.json4s.native.Serialization.{read, write, writePretty} + +object OutOfBandAnnotationSerialization { + + val formats = DefaultFormats.skippingEmptyValues + + def serialize(seq: Seq[OutOfBandAnnotation]): String = writePretty(seq)(formats) + + def deserialize(str: String): Seq[OutOfBandAnnotation] = { + implicit val formats = OutOfBandAnnotationSerialization.formats + read[Seq[OutOfBandAnnotation]](str) + } + +} + diff --git a/src/main/scala/barstools/floorplan/compiler/Pass.scala b/src/main/scala/barstools/floorplan/compiler/Pass.scala new file mode 100644 index 00000000..8198c541 --- /dev/null +++ b/src/main/scala/barstools/floorplan/compiler/Pass.scala @@ -0,0 +1,26 @@ +// See LICENSE for license details +package barstools.floorplan.compiler + +import barstools.floorplan._ + +object Pass { + + def all(opts: FloorplanOptions): Seq[Pass] = { + val instMap = MemInstMap.fromFiles(opts.memInstMapFiles) + val sbAnnos = OutOfBandAnnotationMap.fromFiles(opts.sbAnnoFiles) + Seq( + new TransformMemsPass(instMap), + new OutOfBandAnnotationPass(sbAnnos), + new ReplaceHierarchicalPass(opts.topMod), + new ConstraintPropagationPass(opts.topMod), + new ReplaceMemMacroArrayPass(opts.topMod), + new ResolveConstraintsPass(opts.topMod), + new CalculatePlacementsPass(opts.topMod) + ) + } +} + +abstract class Pass { + def execute(state: FloorplanState): FloorplanState +} + diff --git a/src/main/scala/barstools/floorplan/compiler/ReplaceHierarchicalPass.scala b/src/main/scala/barstools/floorplan/compiler/ReplaceHierarchicalPass.scala new file mode 100644 index 00000000..6ae44821 --- /dev/null +++ b/src/main/scala/barstools/floorplan/compiler/ReplaceHierarchicalPass.scala @@ -0,0 +1,102 @@ +// See LICENSE for license details +package barstools.floorplan.compiler + +import scala.collection.mutable.{HashMap, HashSet} +import barstools.floorplan._ + +class ReplaceHierarchicalPass(val topMod: String) extends Pass { + def execute(state: FloorplanState): FloorplanState = { + + // TODO this pass is for resolving a design to a flattened floorplan. To + // support hierarchical PNR this will need to change + + // Find HierarchicalTop records, then stitch them together + val topMaps = (state.records.flatMap(r => r.element match { + case e: ConstrainedHierarchicalTop => Some((r.fullPath -> r)) + case e: SizedHierarchicalTop => Some((r.fullPath -> r)) + case e: PlacedHierarchicalTop => Some((r.fullPath -> r)) + case _ => None + })).toMap + + val scope = topMaps(topMod).scope + val scopeNameSet = HashSet(state.records.filter(_.scope == scope).map(_.element.name):_*) + + val nonScopePaths = topMaps.filterKeys(_ != topMod).values.map(_.scope) + + def getUniqueName(suggestion: String): String = { + var i = 0 + var tmp = suggestion + s"_${i}" + while (scopeNameSet.contains(tmp)) { + i = i + 1 + tmp = suggestion + s"_${i}" + } + scopeNameSet += tmp + tmp + } + + val renameMap = new HashMap[(String, String), String]() + + def rename(oldScope: String, oldName: String): String = { + if (oldScope == scopeNameSet) { return oldName } + val tup = (oldScope, oldName) + if (renameMap.contains(tup)) { return renameMap(tup) } + val newName = getUniqueName(oldName) + renameMap += (tup -> newName) + newName + } + + def getRelPath(fullPath: Option[String]): Option[String] = fullPath.map { p => + assert(p.startsWith(scope), "Full path must be in scope scope") + p.substring(scope.length) + } + + val newRecords = state.records.flatMap({r => r.element match { + case e: HierarchicalBarrier => + assert(topMaps.contains(r.fullPath), s"All HierarchicalBarriers must have a corresponding HierarchicalTop: ${r.fullPath} is missing") + val tr = topMaps(r.fullPath) + (tr.element match { + case t: ConstrainedHierarchicalTop => + // We replace with two elements (one to replace each name); they'll get optimized away later + // Parent is first (replaces e), child is after (replaces t) + Seq(ConstrainedElasticGrid( + name = rename(r.scope, e.name), + parent = rename(r.scope, e.parent), + xDim = 1, + yDim = 1, + elements = Seq(rename(tr.scope, t.name)), + width = t.width, + height = t.height, + area = t.area, + aspectRatio = t.aspectRatio + ), ConstrainedElasticGrid( + name = rename(tr.scope, t.name), + parent = rename(r.scope, e.name), + xDim = 1, + yDim = 1, + elements = Seq(rename(tr.scope, t.topGroup)), + width = t.width, + height = t.height, + area = t.area, + aspectRatio = t.aspectRatio + )) + case t: SizedHierarchicalTop => ??? // TODO not supported yet + case t: PlacedHierarchicalTop => ??? // TODO not supported yet + case _ => ??? + }).map(newE => FloorplanRecord( + scope = scope, + inst = getRelPath(r.inst.map(_ => r.fullPath)), + ofModule = r.ofModule, + element = newE + )) + case e: ConstrainedHierarchicalTop if r.ofModule != Some(topMod) => Seq() + case e: PlacedHierarchicalTop if r.ofModule != Some(topMod) => Seq() + case e => Seq(r.copy( + scope = scope, + inst = getRelPath(r.inst.map(_ => r.fullPath)), + ofModule = r.ofModule, + element = r.element.mapNames(x => rename(r.scope, x)) + )) + }}) + state.copy(records = newRecords, level = 2) // TODO recalculate level + } +} diff --git a/src/main/scala/barstools/floorplan/compiler/ReplaceMemMacroArrayPass.scala b/src/main/scala/barstools/floorplan/compiler/ReplaceMemMacroArrayPass.scala new file mode 100644 index 00000000..b80fc329 --- /dev/null +++ b/src/main/scala/barstools/floorplan/compiler/ReplaceMemMacroArrayPass.scala @@ -0,0 +1,110 @@ +// See LICENSE for license details +package barstools.floorplan.compiler + +import barstools.floorplan._ + +import scala.math.{BigDecimal, sqrt} + +class ReplaceMemMacroArrayPass(topMod: String) extends Pass { + def execute(state: FloorplanState): FloorplanState = { + val tree = new FloorplanTree(state, topMod) + // TODO this needs some additional options, etc. + val utilization = 0.8 // amount of area to devote to SRAMs + + // For the proof-of-concept, we'll just replace them with an Array that meets whatever box constraints + val nodes = tree.allNodes.values.filter(_.record.element.isInstanceOf[MemMacroArray]) + + nodes.foreach { n => + val e = n.record.element.asInstanceOf[MemMacroArray] + // TODO this assumes that the MemMacroArray contains all the same type of SRAM. This is usually true, but we do nothing to enforce this. + + // Try to size based on the SRAM + val mem0 = tree.getRecord(e.elements(0)).element.asInstanceOf[SizedMacro] + val numMems = e.elements.length + + val defaultWidth = (mem0.width * numMems) / utilization + val defaultHeight = mem0.height + val defaultArea = defaultWidth * defaultHeight + + val (width, height) = e.toConstraints.aspectRatio match { + case c: Unconstrained => + (defaultWidth, defaultHeight) + case c: Impossible => throw new Exception("Illegal impossible constraint") + case c: Constrained => + val h = c.eq.map(q => BigDecimal(sqrt((defaultArea * q).doubleValue))).getOrElse(defaultHeight) + (defaultArea/h, h) + } + + val stackHeight = (height / mem0.height).setScale(0, BigDecimal.RoundingMode.DOWN).toInt + val columns = (numMems + stackHeight - 1) / Seq(stackHeight, 1).max + val paddingColumns = (columns + 1) / 2 + val topPadding = height - (stackHeight*mem0.height) + val xPadding = (width - (columns*mem0.width)) / paddingColumns + val xDim = columns + paddingColumns + val yDim = stackHeight + 1 + + def addSpacer(w: BigDecimal, h: BigDecimal): String = { + val newElement = SizedSpacerRect( + name = tree.getUniqueName("spacer"), + parent = e.name, + width = w, + height = h + ) + n.addChildRecord(FloorplanRecord(n.record.scope, None, None, newElement)) + newElement.name + } + + val elements = Seq.tabulate(xDim*yDim) { i => + val col = (i % xDim) + val row = (i / xDim) + val group = 2*(col / 3) + (row*columns) + val spacerHeight = if (row == yDim-1) topPadding else mem0.height + if (col % 3 == 0) { + if (group >= numMems) { + addSpacer(xPadding, spacerHeight) + } else { + e.elements(group) + } + } else if (col % 3 == 1) { + addSpacer(xPadding, spacerHeight) + } else { + if ((group + 1) >= numMems) { + addSpacer(xPadding, spacerHeight) + } else { + val eOut = e.elements(group + 1) + // Mirror it for proof-of-concept + val myNode = tree.getNode(eOut) + myNode.replace(myNode.record.copy(element = myNode.record.element.asInstanceOf[SizedMacro].copy( + orientation = Orientation.my + ))) + eOut + } + } + } + + val widths = Seq.tabulate(xDim) { i => + if (i % 3 == 0) { + mem0.width + } else if (i % 3 == 1) { + xPadding + } else { + mem0.width + } + } + val heights = Seq.fill(stackHeight)(mem0.height) ++ Seq(topPadding) + + val newElement = SizedGrid( + name = e.name, + parent = e.parent, + xDim = xDim, + yDim = yDim, + elements = elements, // TODO how to arrange these correctly? + widths = widths, + heights = heights + ) + n.replace(n.record.copy(element = newElement)) + } + + tree.toState + } +} diff --git a/src/main/scala/barstools/floorplan/compiler/ResolveConstraintsPass.scala b/src/main/scala/barstools/floorplan/compiler/ResolveConstraintsPass.scala new file mode 100644 index 00000000..d17b3341 --- /dev/null +++ b/src/main/scala/barstools/floorplan/compiler/ResolveConstraintsPass.scala @@ -0,0 +1,90 @@ +// See LICENSE for license details +package barstools.floorplan.compiler + +import barstools.floorplan._ + +class ResolveConstraintsPass(topMod: String) extends Pass { + def resolveConstraints(elt: ConstrainedRectLike): (BigDecimal, BigDecimal) = { + // TODO this is a temporary strategy + elt.toConstraints.resolveMinDimensions() + } + + def execute(state: FloorplanState): FloorplanState = { + val tree = new FloorplanTree(state, topMod) + + // TODO this isn't a correct implementation; it just resolves the constraints seen + // by any individual element rather than looking at the entirety of the floorplan + + // Bottom-up pass + tree.traverseMapPost { node => + node.record.element match { + case e: SizedRectLike => None // Already sized + case e: ConstrainedSpacerRect => + val (width, height) = resolveConstraints(e) + Some(node.record.copy(element = SizedSpacerRect( + e.name, + e.parent, + width, + height + ))) + case e: ConstrainedLogicRect => + val (width, height) = resolveConstraints(e) + Some(node.record.copy(element = SizedLogicRect( + e.name, + e.parent, + width, + height, + e.hardBoundary + ))) + case e: ConstrainedHierarchicalTop => + val (width, height) = resolveConstraints(e) + Some(node.record.copy(element = SizedHierarchicalTop( + e.name, + e.topGroup, + width, + height, + e.margins, + e.hardBoundary + ))) + case e: ConstrainedWeightedGrid => + // TODO make not repetitive + // Preserve ordering of children and convert to 2d Seq + // FIXME this is a hack that assumes all of the elements are equally constrained in a row (height) or column (width) + // This is true for the current implementation of the constraints propagation but not generally true + val childDims: Seq[(BigDecimal, BigDecimal)] = e.elements.map(x => tree.getRecord(x).element.toConstraints.resolveMinDimensions()) + val widths = childDims.take(e.xDim).map(_._1) + val heights = childDims.grouped(e.xDim).map(_(0)._2).toSeq + + Some(node.record.copy(element = SizedGrid( + e.name, + e.parent, + e.xDim, + e.yDim, + e.elements, + widths, + heights + ))) + case e: ConstrainedElasticGrid => + // Preserve ordering of children and convert to 2d Seq + // FIXME this is a hack that assumes all of the elements are equally constrained in a row (height) or column (width) + // This is true for the current implementation of the constraints propagation but not generally true + val childDims: Seq[(BigDecimal, BigDecimal)] = e.elements.map(x => tree.getRecord(x).element.toConstraints.resolveMinDimensions()) + val widths = childDims.take(e.xDim).map(_._1) + val heights = childDims.grouped(e.xDim).map(_(0)._2).toSeq + + Some(node.record.copy(element = SizedGrid( + e.name, + e.parent, + e.xDim, + e.yDim, + e.elements, + widths, + heights + ))) + case e => throw new Exception("Illegal element type") + } + } + + tree.toState + } +} diff --git a/src/main/scala/barstools/floorplan/compiler/TransformMemsPass.scala b/src/main/scala/barstools/floorplan/compiler/TransformMemsPass.scala new file mode 100644 index 00000000..bb2429a0 --- /dev/null +++ b/src/main/scala/barstools/floorplan/compiler/TransformMemsPass.scala @@ -0,0 +1,72 @@ +// See LICENSE for license details +package barstools.floorplan.compiler + +import firrtl.annotations.TargetToken.{Instance, OfModule} + +import barstools.floorplan._ + +import scala.collection.{Map, Set} +import scala.collection.mutable.{HashMap, HashSet} + +class TransformMemsPass(instMap: Map[OfModule, Seq[(Instance, OfModule)]]) extends Pass { + + val nameSet = new HashSet[String]() + val renameMap = new HashMap[String, Set[String]]() + + def getUniqueName(suggestion: String): String = { + // This is ugly and probably slow + var i = 0 + var tempName = suggestion + s"_${i}" + while (nameSet.contains(tempName)) { + tempName = suggestion + s"_${i}" + i += 1 + } + nameSet.add(tempName) + tempName + } + + def execute(state: FloorplanState): FloorplanState = { + nameSet ++= state.records.map(_.element.name).toSet + // Need to update in two passes + val newRecords = state.records.flatMap({ record => + record.element match { + case e: MemElement => + // TODO fail gracefully if key does not exist + instMap(OfModule(record.ofModule.get)).map { case (inst: Instance, ofMod: OfModule) => + nameSet.remove(e.name) + val element = AbstractMacro(getUniqueName(e.name + "_" + ofMod.value), e.parent) + renameMap.update(e.name, renameMap.getOrElse(e.name, Set()) ++ Set(element.name)) + FloorplanRecord( + scope = record.scope, + inst = record.inst.map(_ + "/" + inst.value), + ofModule = Some(ofMod.value), + element = element + ) + } + case _ => Seq(record) + } + }).map({ record => + record.element match { + case e: MemElementArray => + val element = MemMacroArray( + name = e.name, + parent = e.parent, + elements = e.elements.flatMap(x => renameMap(x)), + width = e.width, + height = e.height, + area = e.area, + aspectRatio = e.aspectRatio + ) + FloorplanRecord( + scope = record.scope, + inst = record.inst, // should always be None + ofModule = None, + element = element + ) + case _ => record + } + }) + state.copy(records = newRecords, level = 3) // TODO recalculate level? + } +} + diff --git a/src/main/scala/barstools/floorplan/firrtl/Annotations.scala b/src/main/scala/barstools/floorplan/firrtl/Annotations.scala new file mode 100644 index 00000000..8fd30fe0 --- /dev/null +++ b/src/main/scala/barstools/floorplan/firrtl/Annotations.scala @@ -0,0 +1,38 @@ +// See LICENSE for license details +package barstools.floorplan.firrtl + + +import barstools.floorplan.{Element, Group} +import firrtl.annotations._ +import firrtl.stage.{RunFirrtlTransformAnnotation} + +trait FloorplanAnnotation extends Annotation { + val fpir: String +} + +case class InstanceFloorplanAnnotation(targets: Seq[Seq[Target]], fpir: String) extends MultiTargetAnnotation with FloorplanAnnotation { + def duplicate(t: Seq[Seq[Target]]) = this.copy(t, fpir) +} + +case class MemFloorplanAnnotation(targets: Seq[Seq[Target]], fpir: String) extends MultiTargetAnnotation with FloorplanAnnotation { + def duplicate(t: Seq[Seq[Target]]) = this.copy(t, fpir) +} + +case class NoReferenceFloorplanAnnotation(target: Target, fpir: String) extends SingleTargetAnnotation[Target] with FloorplanAnnotation { + def duplicate(t: Target) = this.copy(t, fpir) +} + +object InstanceFloorplanAnnotation { + def apply(targets: Seq[Seq[Target]], element: Element): InstanceFloorplanAnnotation = InstanceFloorplanAnnotation(targets, element.serialize) +} + +object MemFloorplanAnnotation { + def apply(targets: Seq[Seq[Target]], element: Element): MemFloorplanAnnotation = MemFloorplanAnnotation(targets, element.serialize) +} + +object NoReferenceFloorplanAnnotation { + def apply(target: Target, element: Element): NoReferenceFloorplanAnnotation = NoReferenceFloorplanAnnotation(target, element.serialize) +} + +case class FloorplanIRFileAnnotation(value: String) extends NoTargetAnnotation + diff --git a/src/main/scala/barstools/floorplan/firrtl/Passes.scala b/src/main/scala/barstools/floorplan/firrtl/Passes.scala new file mode 100644 index 00000000..afe0db56 --- /dev/null +++ b/src/main/scala/barstools/floorplan/firrtl/Passes.scala @@ -0,0 +1,114 @@ +// See LICENSE for license details +package barstools.floorplan.firrtl + +import barstools.floorplan.{FloorplanSerialization, FloorplanRecord, FloorplanState} +import firrtl.{CircuitState, Transform, DependencyAPIMigration, VerilogEmitter, AnnotationSeq} +import firrtl.options.{Dependency, RegisteredTransform, ShellOption} +import firrtl.analyses.{IRLookup} +import firrtl.annotations.{InstanceTarget, ReferenceTarget, ModuleTarget, Target, IsComponent} +import firrtl.annotations.TargetToken.{Instance, OfModule} + +// NOTE: If you rename/add this transform, don't forget to update META-INF +// See the @note in the RegisteredTransform documentation +class GenerateFloorplanIRPass extends Transform with RegisteredTransform with DependencyAPIMigration { + + override def prerequisites = Seq(Dependency[VerilogEmitter]) + override def optionalPrerequisites = Nil + override def dependents = Nil + override def invalidates(xform: Transform) = false + + val options = Seq( + new ShellOption[String]( + longOption = "floorplan-ir-file", + toAnnotationSeq = (a: String) => Seq(FloorplanIRFileAnnotation(a)), + helpText = s"Set the floorplan IR file name" + ) + ) + + def execute(state: CircuitState): CircuitState = { + + def getInstancePath(t: Option[InstanceTarget]): String = t map { it => + (Seq(it.module) ++ it.asPath.toList.map(_._1.value)).mkString("/") + } getOrElse state.circuit.main + + def getRelativePath(scope: Option[InstanceTarget], inst: Option[IsComponent]): String = { + val scopePath = scope.map(_.asPath).getOrElse(Seq()) + val instPath = inst.map(_.asPath).getOrElse(Seq()) + assert(instPath.take(scopePath.length) == scopePath, s"InstanceTarget ${instPath} must be inside ${scopePath}") + val pathStr = instPath.drop(scopePath.length).toList.map(_._1.value).mkString("/") + inst.map(_ match { + case x: InstanceTarget => pathStr + case x: ReferenceTarget => pathStr + "." + x.ref + case _ => ??? // Shouldn't exist + }) getOrElse "" + } + + def newRecord(path: String, ref: Option[String], ofModule: Option[String], anno: FloorplanAnnotation) = + FloorplanRecord(path, ref, ofModule, FloorplanSerialization.deserialize(anno.fpir)) + + val list = state.annotations.collect({ + case x: NoReferenceFloorplanAnnotation => + val (scopeTarget, ofModule) = x.target match { + case y: InstanceTarget => (Some(y), Some(y.ofModule)) + case y: ModuleTarget => + assert(y.module == state.circuit.main, "ModuleTarget is only supported for the top module") + (Option.empty[InstanceTarget], Some(y.module)) + case _ => ??? + } + newRecord(getInstancePath(scopeTarget), None, ofModule, x) + case x: InstanceFloorplanAnnotation if x.targets.flatten.length == 2 => + val scopeTarget = x.targets(0)(0) match { + case y: InstanceTarget => Some(y) + case y: ModuleTarget => + assert(y.module == state.circuit.main, "ModuleTarget is only supported for the top module") + Option.empty[InstanceTarget] + case _ => ??? + } + val (instTarget, ofModule) = x.targets(1)(0) match { + case y: InstanceTarget => (Some(y), Some(y.ofModule)) + case y: ModuleTarget => + assert(y.module == state.circuit.main, "ModuleTarget is only supported for the top module") + (Option.empty[InstanceTarget], Some(y.module)) + case _ => ??? + } + newRecord(getInstancePath(scopeTarget), Some(getRelativePath(scopeTarget, instTarget)), ofModule, x) + case x: MemFloorplanAnnotation if x.targets.flatten.length == 2 => + val scopeTarget = x.targets(0)(0) match { + case y: InstanceTarget => Some(y) + case y: ModuleTarget => + assert(y.module == state.circuit.main, "ModuleTarget is only supported for the top module") + Option.empty[InstanceTarget] + case _ => ??? + } + val refTarget = x.targets(1)(0).asInstanceOf[ReferenceTarget] + // Note: This assumes specific behavior from ReplSeqMem, namely that it replaces the Mem reference with + // a wrapper instance named ${ext} that instantiates an external bbox named ${ext}_ext + val mem = IRLookup(state.circuit).declaration(refTarget) match { + case m: firrtl.ir.DefInstance => m.module + case _ => throw new Exception("Something went wrong, Mems should become ExtModule instances") + } + val ext = mem+"_ext" + // TODO do we want to replace this in the output annotation file... ? + val newTarget = InstanceTarget( + circuit=refTarget.circuit, + module=refTarget.module, + instance=ext, + ofModule=ext, + path=refTarget.path :+ (Instance(refTarget.ref), OfModule(mem))) + newRecord(getInstancePath(scopeTarget), Some(getRelativePath(scopeTarget, Some(newTarget))), Some(ext), x) + }) + + val filename = state.annotations.collectFirst({ + case x: FloorplanIRFileAnnotation => x.value + }).getOrElse { + val opt = options.head.longOption + throw new Exception(s"Did not specify a filename for GenerateFloorplanIRPass. Please provide a FloorplanIRFileAnnotation or use the --${opt} option.") + } + val writer = new java.io.FileWriter(filename) + writer.write(FloorplanState.serialize(list)) + writer.close() + + state + } +} + diff --git a/src/main/scala/barstools/floorplan/hammer/HammerIR.scala b/src/main/scala/barstools/floorplan/hammer/HammerIR.scala new file mode 100644 index 00000000..cfabd050 --- /dev/null +++ b/src/main/scala/barstools/floorplan/hammer/HammerIR.scala @@ -0,0 +1,122 @@ +// See LICENSE for license details +package barstools.floorplan.hammer + +import barstools.floorplan._ + +import java.io.{Writer} +import scala.math.{BigInt, BigDecimal} + +// This implements a small subset of HammerIR + +object HammerIR { + + def fromFloorplanState(state: FloorplanState): HammerIR = { + assert(state.level == 0, "Can only convert level 0 FloorplanState") + val placementConstraints = state.records.map({ record => + record.element match { + case c: PlacedMacro => + Some(PlacementConstraint( + path = record.fullPath, + typ = PlacementType.hardmacro, + orientation = Some(c.orientation), + x = toMicrons(c.x), + y = toMicrons(c.y), + create_physical = Some(false), + width = toMicrons(c.width), + height = toMicrons(c.height), + master = record.ofModule, + margins = None, + top_layer = None, // TODO need this for macros + layers = None, // TODO need this for macros + obs_types = None // TODO need this for macros + )) + case c: PlacedLogicRect => + Some(PlacementConstraint( + path = record.fullPath, + typ = PlacementType.placement, + orientation = Some(Orientation.r0), // TODO represent this in FPIR + x = toMicrons(c.x), + y = toMicrons(c.y), + create_physical = None, + width = toMicrons(c.width), + height = toMicrons(c.height), + master = None, + margins = None, + top_layer = None, + layers = None, + obs_types = None + )) + case c: PlacedHierarchicalTop => + Some(PlacementConstraint( + path = record.fullPath, + typ = PlacementType.toplevel, + orientation = None, + x = toMicrons(c.x), + y = toMicrons(c.y), + create_physical = None, + width = toMicrons(c.width), + height = toMicrons(c.height), + master = None, + margins = Some(PlacementMargins(c.margins)), + top_layer = None, + layers = None, + obs_types = None + )) + case c => None + } + }).filter(_.isDefined).map(_.get) + HammerIR(placementConstraints = placementConstraints) + } + + def serialize(h: HammerIR): String = HammerSerialization.serialize(h) + def deserialize(s: String): HammerIR = HammerSerialization.deserialize(s) + def toMicrons(l: BigDecimal): Double = l.toDouble +} + +final case class HammerIR private[hammer] ( + placementConstraints: Seq[PlacementConstraint] +) + +final case class PlacementConstraint private[hammer] ( + path: String, + typ: PlacementType.Value, // type is a keyword, so we use typ and rename it in the serializer + orientation: Option[Orientation.Value], + // TODO these should ideally all be decimal types, not doubles + x: Double, + y: Double, + width: Double, + height: Double, + // End TODO + master: Option[String], + create_physical: Option[Boolean], + margins: Option[PlacementMargins], + top_layer: Option[String], + layers: Option[Seq[String]], + obs_types: Option[Seq[ObstructionType.Value]] +) + +object PlacementType extends Enumeration { + val dummy, placement, toplevel, hardmacro, hierarchical, obstruction = Value +} + +object ObstructionType extends Enumeration { + val place, route, power = Value +} + +final case class PlacementMargins private[hammer] ( + // TODO these should ideally all be decimal types, not doubles + left: Double, + right: Double, + top: Double, + bottom: Double +) + +object PlacementMargins { + def apply(m: barstools.floorplan.Margins): PlacementMargins = PlacementMargins( + left=m.left.toDouble, + right=m.right.toDouble, + top=m.top.toDouble, + bottom=m.bottom.toDouble + ) +} + diff --git a/src/main/scala/barstools/floorplan/hammer/HammerSerialization.scala b/src/main/scala/barstools/floorplan/hammer/HammerSerialization.scala new file mode 100644 index 00000000..39e12d5c --- /dev/null +++ b/src/main/scala/barstools/floorplan/hammer/HammerSerialization.scala @@ -0,0 +1,42 @@ +// See LICENSE for license details +package barstools.floorplan.hammer + +import barstools.floorplan.Orientation + +import org.json4s._ +import org.json4s.FieldSerializer.renameTo +import org.json4s.native.Serialization.{read, write, writePretty} +import org.json4s.JsonAST.JString +import org.json4s.ext.EnumNameSerializer + +object HammerSerialization { + + /* + // Custom serializer for PlacementType enums + class PlacementTypeSerializer extends CustomSerializer[PlacementType.Value](format => ( + { case JString(s) => PlacementType.values.withName(s) }, + { case v: PlacementType.Value => JString(v.toString) } + )) + // Custom serializer for Orientation enums + class OrientationSerializer extends CustomSerializer[Orientation.Value](format => ( + { case JString(s) => Orientation.values.withName(s) }, + { case v: Orientation.Value => JString(v.toString) } + )) + */ + + val formats = + DefaultFormats.skippingEmptyValues + + new EnumNameSerializer(PlacementType) + + new EnumNameSerializer(Orientation) + + new EnumNameSerializer(ObstructionType) + + FieldSerializer[PlacementConstraint](renameTo("typ", "type")) + + FieldSerializer[HammerIR](renameTo("placementConstraints", "vlsi.inputs.placement_constraints")) + + def serialize(state: HammerIR): String = writePretty(state)(formats) + + def deserialize(str: String): HammerIR = { + implicit val formats = HammerSerialization.formats + read[HammerIR](str) + } + +} diff --git a/src/main/scala/barstools/floorplan/package.scala b/src/main/scala/barstools/floorplan/package.scala new file mode 100644 index 00000000..e9ce1c68 --- /dev/null +++ b/src/main/scala/barstools/floorplan/package.scala @@ -0,0 +1,18 @@ +// See LICENSE for license details +package barstools +package object floorplan { + +import scala.math.{BigInt, BigDecimal} +import scala.collection.Seq + +def gcd(a: BigDecimal, b: BigDecimal): BigDecimal = { + val scaleFactor = 1 << Seq(a.scale, b.scale).max + val scaledGCD = (a*scaleFactor).toBigInt.gcd((b*scaleFactor).toBigInt) + BigDecimal(scaledGCD, scaleFactor) +} + +def lcm(a: BigDecimal, b: BigDecimal): BigDecimal = (a*b)/gcd(a,b) + +type PrimitiveConstraint = Constraint + +} diff --git a/src/main/scala/barstools/macros/MacroCompiler.scala b/src/main/scala/barstools/macros/MacroCompiler.scala index a681623c..2011e609 100644 --- a/src/main/scala/barstools/macros/MacroCompiler.scala +++ b/src/main/scala/barstools/macros/MacroCompiler.scala @@ -118,7 +118,7 @@ object MacroCompilerAnnotation { memFormat: Option[String], lib: Option[String], hammerIR: Option[String], - instMap: Option[String], + instMap: Option[String], costMetric: CostMetric, mode: CompilerMode, useCompiler: Boolean, @@ -321,9 +321,9 @@ class MacroCompilerPass( // Depth mapping val stmts = ArrayBuffer[Statement]() - val outputs = HashMap[String, ArrayBuffer[(Expression, Expression)]]() - val selects = HashMap[String, Expression]() - val selectRegs = HashMap[String, Expression]() + val outputs = mutable.HashMap[String, ArrayBuffer[(Expression, Expression)]]() + val selects = mutable.HashMap[String, Expression]() + val selectRegs = mutable.HashMap[String, Expression]() // Store (instanceName, moduleName) tuples for use by floorplanning val insts = ArrayBuffer[(Instance, OfModule)]() /* Palmer: If we've got a parallel memory then we've got to take the @@ -646,11 +646,7 @@ class MacroCompilerPass( // Try to compile mem against each lib in libs, keeping track of the // best compiled version, external lib used, and cost. -<<<<<<< HEAD:src/main/scala/barstools/macros/MacroCompiler.scala - val (best, _) = fullLibs.foldLeft(None: Option[(Module, Macro)], Double.MaxValue) { -======= val (best, cost) = (fullLibs.foldLeft(None: Option[(Module, Macro, Seq[(Instance, OfModule)])], Double.MaxValue)) { ->>>>>>> Add text file that lists the macrocompiler mapping:macros/src/main/scala/barstools/macros/MacroCompiler.scala case ((best, cost), lib) if mem.src.ports.size != lib.src.ports.size => /* Palmer: FIXME: This just assumes the Chisel and vendor ports are in the same * order, but I'm starting with what actually gets generated. */ @@ -682,28 +678,12 @@ class MacroCompilerPass( ) else modules -<<<<<<< HEAD:src/main/scala/barstools/macros/MacroCompiler.scala - case Some((mod, bb)) => - hammerIR match { - case Some(f) => - val hammerIRWriter = new FileWriter(new File(f), !firstLib) - if (firstLib) hammerIRWriter.write("[\n") - hammerIRWriter.write(bb.src.toJSON().toString()) - hammerIRWriter.write("\n,\n") - hammerIRWriter.close() - firstLib = false - case None => - } - modules.filterNot(m => m.name == mod.name || m.name == bb.blackbox.name) ++ Seq(mod, bb.blackbox) -======= - } case Some((mod, bb, insts)) => // Add hammerIR hammerIRBuffer.append(bb.src.toJSON().toString()) // Add insts instMapBuffer.append(mod.name + " " + insts.map(x => x._1.value + ":" + x._2.value).mkString(" ")) (modules.filterNot(m => m.name == mod.name || m.name == bb.blackbox.name)) ++ Seq(mod, bb.blackbox) ->>>>>>> Add text file that lists the macrocompiler mapping:macros/src/main/scala/barstools/macros/MacroCompiler.scala } } case _ => c.modules @@ -723,6 +703,7 @@ class MacroCompilerPass( writer.close() } c.copy(modules = modules) + c.copy(modules = modules) } } @@ -954,19 +935,6 @@ object MacroCompiler extends App { ) ) -<<<<<<< HEAD:src/main/scala/barstools/macros/MacroCompiler.scala - params.get(HammerIR) match { - case Some(hammerIRFile: String) => - val lines = FileUtils.getLines(hammerIRFile).toList - val hammerIRWriter = new FileWriter(new File(hammerIRFile)) - // JSON means we need to destroy the last comma :( - lines.dropRight(1).foreach(l => hammerIRWriter.write(l + "\n")) - hammerIRWriter.write("]\n") - hammerIRWriter.close() - case None => - } -======= ->>>>>>> Add text file that lists the macrocompiler mapping:macros/src/main/scala/barstools/macros/MacroCompiler.scala } else { // Warn user System.err.println("WARNING: Empty *.mems.conf file. No memories generated.") diff --git a/src/main/scala/barstools/macros/SynFlopsPass.scala b/src/main/scala/barstools/macros/SynFlopsPass.scala index fa3b0057..bba03dae 100644 --- a/src/main/scala/barstools/macros/SynFlopsPass.scala +++ b/src/main/scala/barstools/macros/SynFlopsPass.scala @@ -47,7 +47,7 @@ class SynFlopsPass(synflops: Boolean, libs: Seq[Macro]) extends firrtl.passes.Pa ) ) ) - val mod_macro = (new MacroCompilerPass(None, None, None, None, None)).compile(lib, lib_macro) + val mod_macro = new MacroCompilerPass(None, None, None, None, None).compile(lib, lib_macro) val (real_mod, real_macro, insts) = mod_macro.get val mem = DefMemory( diff --git a/src/main/scala/barstools/tapeout/transforms/FloorplanReParentTransform.scala b/src/main/scala/barstools/tapeout/transforms/FloorplanReParentTransform.scala new file mode 100644 index 00000000..d2a4a240 --- /dev/null +++ b/src/main/scala/barstools/tapeout/transforms/FloorplanReParentTransform.scala @@ -0,0 +1,58 @@ +// See LICENSE for license details +package barstools.tapeout.transforms +// This needs to be in the tapeout package, not floorplan, because of the build dependencies (floorplan cannot depend on tapeout) + +import barstools.floorplan.firrtl.{NoReferenceFloorplanAnnotation, InstanceFloorplanAnnotation, MemFloorplanAnnotation, FloorplanAnnotation} +import firrtl.annotations.{InstanceTarget, ReferenceTarget, ModuleTarget, Target, IsComponent} +import firrtl.{CircuitState, Transform, DependencyAPIMigration, AnnotationSeq} +import firrtl.stage.Forms +import firrtl.options.Dependency +import firrtl.stage.TransformManager.TransformDependency + +class FloorplanReParentTransform extends Transform with DependencyAPIMigration { + + override def prerequisites: Seq[TransformDependency] = Forms.HighForm + override def optionalPrerequisites: Seq[TransformDependency] = Seq.empty + override def optionalPrerequisiteOf: Seq[TransformDependency] = Seq(Dependency[ReParentCircuit]) + override def invalidates(a: Transform): Boolean = false + + def transformTarget(t: Target, top: String): Target = t match { + case it: InstanceTarget => + if (it.ofModule == top) it.ofModuleTarget else it + case ot => ot + } + + def transformTargets(t: Seq[Seq[Target]], top: String): Seq[Seq[Target]] = { + t.map(_.map(x => transformTarget(x, top))) + } + + def execute(state: CircuitState): CircuitState = { + val newTop = state.annotations.collectFirst { + case ReParentCircuitAnnotation(x) => x.module + } + + // Convert the annotated top to a ModuleTarget, otherwise leave them alone and let ReParentCircuit handle them + val newAnnos = newTop.map(top => AnnotationSeq(state.annotations.toSeq.map { _ match { + // We probably want to check the sizes of these first + case a: NoReferenceFloorplanAnnotation => + a.target match { + case t: InstanceTarget => a.copy(target = transformTarget(t, top)) + case t => a + } + case a: InstanceFloorplanAnnotation => + a.targets(0)(0) match { + case t: InstanceTarget => a.copy(targets = transformTargets(a.targets, top)) + case t => a + } + case a: MemFloorplanAnnotation => + a.targets(0)(0) match { + case t: InstanceTarget => a.copy(targets = transformTargets(a.targets, top)) + case t => a + } + case _: FloorplanAnnotation => ??? + case a => a + }})).getOrElse(state.annotations) + + state.copy(annotations = newAnnos) + } +} diff --git a/src/main/scala/barstools/tapeout/transforms/GenerateTopAndHarness.scala b/src/main/scala/barstools/tapeout/transforms/GenerateTopAndHarness.scala index 459b36de..1f3d2d92 100644 --- a/src/main/scala/barstools/tapeout/transforms/GenerateTopAndHarness.scala +++ b/src/main/scala/barstools/tapeout/transforms/GenerateTopAndHarness.scala @@ -24,8 +24,7 @@ private class GenerateTopAndHarness(annotations: AnnotationSeq) extends LazyLogg val harnessOutput: Option[String] = annotations.collectFirst { case HarnessOutputAnnotation(h) => h } val topDotfOut: Option[String] = annotations.collectFirst { case TopDotfOutAnnotation(h) => h } val harnessDotfOut: Option[String] = annotations.collectFirst { case HarnessDotfOutAnnotation(h) => h } - // Note: this file name isn't actually used here, we just test to see if the annotaiton exists to run the pass - val floorplanFile: Option[String] = annotations.collectFirst { case FloorplanIRFileAnnotation(h) => h } + val floorplanFile: Option[String] = annotations.collectFirst { case FloorplanIRFileAnnotation(h) => h } val annoFiles: List[String] = annotations.flatMap { case InputAnnotationFileAnnotation(f) => Some(f) From 886393f3834281246ce9a4d3f252030772f53866 Mon Sep 17 00:00:00 2001 From: Harrison Liew Date: Wed, 6 Apr 2022 21:00:07 -0700 Subject: [PATCH 69/73] delete moved files --- floorplan/README | 61 --- .../firrtl.options.RegisteredTransform | 1 - .../barstools/floorplan/Constraints.scala | 348 ------------ .../floorplan/FloorplanSerialization.scala | 39 -- .../barstools/floorplan/FloorplanState.scala | 50 -- .../main/scala/barstools/floorplan/IR.scala | 449 ---------------- .../barstools/floorplan/chisel/API.scala | 494 ------------------ .../floorplan/chisel/FloorplanAspect.scala | 27 - .../barstools/floorplan/chisel/package.scala | 11 - .../compiler/CalculatePlacementsPass.scala | 92 ---- .../compiler/ConstraintPropagationPass.scala | 174 ------ .../compiler/FloorplanCompiler.scala | 76 --- .../floorplan/compiler/FloorplanTree.scala | 138 ----- .../floorplan/compiler/MemInstMap.scala | 29 - .../compiler/OutOfBandAnnotation.scala | 46 -- .../compiler/OutOfBandAnnotationPass.scala | 94 ---- .../OutOfBandAnnotationSerialization.scala | 19 - .../barstools/floorplan/compiler/Pass.scala | 26 - .../compiler/ReplaceHierarchicalPass.scala | 102 ---- .../compiler/ReplaceMemMacroArrayPass.scala | 110 ---- .../compiler/ResolveConstraintsPass.scala | 90 ---- .../compiler/TransformMemsPass.scala | 72 --- .../floorplan/firrtl/Annotations.scala | 38 -- .../barstools/floorplan/firrtl/Passes.scala | 114 ---- .../barstools/floorplan/hammer/HammerIR.scala | 122 ----- .../hammer/HammerSerialization.scala | 42 -- .../scala/barstools/floorplan/package.scala | 18 - .../FloorplanReParentTransform.scala | 58 -- 28 files changed, 2940 deletions(-) delete mode 100644 floorplan/README delete mode 100644 floorplan/src/main/resources/META-INF/services/firrtl.options.RegisteredTransform delete mode 100644 floorplan/src/main/scala/barstools/floorplan/Constraints.scala delete mode 100644 floorplan/src/main/scala/barstools/floorplan/FloorplanSerialization.scala delete mode 100644 floorplan/src/main/scala/barstools/floorplan/FloorplanState.scala delete mode 100644 floorplan/src/main/scala/barstools/floorplan/IR.scala delete mode 100644 floorplan/src/main/scala/barstools/floorplan/chisel/API.scala delete mode 100644 floorplan/src/main/scala/barstools/floorplan/chisel/FloorplanAspect.scala delete mode 100644 floorplan/src/main/scala/barstools/floorplan/chisel/package.scala delete mode 100644 floorplan/src/main/scala/barstools/floorplan/compiler/CalculatePlacementsPass.scala delete mode 100644 floorplan/src/main/scala/barstools/floorplan/compiler/ConstraintPropagationPass.scala delete mode 100644 floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanCompiler.scala delete mode 100644 floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanTree.scala delete mode 100644 floorplan/src/main/scala/barstools/floorplan/compiler/MemInstMap.scala delete mode 100644 floorplan/src/main/scala/barstools/floorplan/compiler/OutOfBandAnnotation.scala delete mode 100644 floorplan/src/main/scala/barstools/floorplan/compiler/OutOfBandAnnotationPass.scala delete mode 100644 floorplan/src/main/scala/barstools/floorplan/compiler/OutOfBandAnnotationSerialization.scala delete mode 100644 floorplan/src/main/scala/barstools/floorplan/compiler/Pass.scala delete mode 100644 floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceHierarchicalPass.scala delete mode 100644 floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceMemMacroArrayPass.scala delete mode 100644 floorplan/src/main/scala/barstools/floorplan/compiler/ResolveConstraintsPass.scala delete mode 100644 floorplan/src/main/scala/barstools/floorplan/compiler/TransformMemsPass.scala delete mode 100644 floorplan/src/main/scala/barstools/floorplan/firrtl/Annotations.scala delete mode 100644 floorplan/src/main/scala/barstools/floorplan/firrtl/Passes.scala delete mode 100644 floorplan/src/main/scala/barstools/floorplan/hammer/HammerIR.scala delete mode 100644 floorplan/src/main/scala/barstools/floorplan/hammer/HammerSerialization.scala delete mode 100644 floorplan/src/main/scala/barstools/floorplan/package.scala delete mode 100644 tapeout/src/main/scala/barstools/tapeout/transforms/FloorplanReParentTransform.scala diff --git a/floorplan/README b/floorplan/README deleted file mode 100644 index 514bedef..00000000 --- a/floorplan/README +++ /dev/null @@ -1,61 +0,0 @@ - -Placement/Boundary constraints - - -Schema: - -- `Element` is the base abstract class - -- `Primitive` extends `Element` (abstract) - - name (String) - - target (InstanceTarget) ?? (some way of tying this to an instance) - -- `Rect` extends `Primitive` (abstract) - - x (LengthUnit) - - y (LengthUnit) - - w (LengthUnit) - - h (LengthUnit) - - rot (Rotation) - -- `Macro` extends `Rect` - -- `RouteBlockage` extends `Rect` - - layer (String) - -- `PlaceBlockage` extends `Rect` - -- `SeqMem` extends `Rect` - -- `Group` extends `Element` (abstract) - -- `Grid` extends `Group` - - elements (Array2D[Element]) - -- `HorizontalArray` extends `Array` - - elements.height = 1 - -- `VerticalArray` extends `Array` - - elements.width = 1 - -- `LayoutOptions` extends `Element` (abstract) - - layouts (List[Group]) - -- `PriorityLayoutOptions` extends `LayoutOptions` - - - -IR Level 0 -- `Rect`s must contain x, y, rot - - -IR Level 1 -- `Rect`s must contain w, h - - -IR Level 2 -- `Rect`s must contain name, target, type - - - -- Need to have an "inline" way of saying "Replace the floorplan of this block with some external hammer IR" - diff --git a/floorplan/src/main/resources/META-INF/services/firrtl.options.RegisteredTransform b/floorplan/src/main/resources/META-INF/services/firrtl.options.RegisteredTransform deleted file mode 100644 index 65d879eb..00000000 --- a/floorplan/src/main/resources/META-INF/services/firrtl.options.RegisteredTransform +++ /dev/null @@ -1 +0,0 @@ -barstools.floorplan.firrtl.GenerateFloorplanIRPass diff --git a/floorplan/src/main/scala/barstools/floorplan/Constraints.scala b/floorplan/src/main/scala/barstools/floorplan/Constraints.scala deleted file mode 100644 index 8f5c89ee..00000000 --- a/floorplan/src/main/scala/barstools/floorplan/Constraints.scala +++ /dev/null @@ -1,348 +0,0 @@ -// See LICENSE for license details -package barstools.floorplan - -import scala.math.{BigInt, BigDecimal, sqrt} - -sealed trait Constraint { - def and(that: Constraint): Constraint - def +(that: Constraint): Constraint - def *(that: BigDecimal): Constraint - def /(that: BigDecimal): Constraint - def test(value: BigDecimal): Boolean - def minimize: Constraint - - def resolveMin: BigDecimal = this.minimize match { - case c: Impossible => throw new Exception("Cannot reduce impossible constraint. TODO provide more detailed debug info.") - case c: Unconstrained => BigDecimal(0) - case c: Constrained => - c.eq.foreach { x => return x } - c.geq.foreach { x => - c.mof.foreach { m => - val n = (x/m).setScale(0, BigDecimal.RoundingMode.UP) - m*n - } - return x - } - BigDecimal(0) - } - - def isConstrained: Boolean -} - -final class Unconstrained extends Constraint { - def and(that: Constraint) = that - def +(that: Constraint) = that // ??? - def *(that: BigDecimal) = this - def /(that: BigDecimal) = this - def test(value: BigDecimal) = true - def minimize = this - def isConstrained = false -} - -object Unconstrained { - private val u = new Unconstrained - def apply() = u -} - -object UnconstrainedSeq { - def apply(x: Int) = Seq.fill(x) { Unconstrained() } -} - -// TODO add a reason? -final class Impossible extends Constraint { - def and(that: Constraint) = this - def +(that: Constraint) = this - def *(that: BigDecimal) = this - def /(that: BigDecimal) = this - def test(value: BigDecimal) = false - def minimize = this - def isConstrained = true -} - -object Impossible { - private val i = new Impossible - def apply() = i -} - -final case class Constrained( - eq: Option[BigDecimal] = None, - geq: Option[BigDecimal] = None, - leq: Option[BigDecimal] = None, - mof: Option[BigDecimal] = None -) extends Constraint { - - def isConstrained: Boolean = eq.isDefined || geq.isDefined || leq.isDefined || mof.isDefined - - def and(that: Constraint): Constraint = { - that match { - case that: Unconstrained => this - case that: Impossible => that - case that: Constrained => - - // Combine raw constraints - val newMof = if (this.mof.isDefined && that.mof.isDefined) { - Some(lcm(this.mof.get, that.mof.get)) - } else { - this.mof.orElse(that.mof) - } - - val newLeq = if (this.leq.isDefined && that.leq.isDefined) { - Some(Seq(this.leq.get, that.leq.get).min) - } else { - this.leq.orElse(that.leq) - } - - val newGeq = if (this.geq.isDefined && that.geq.isDefined) { - Some(Seq(this.geq.get, that.geq.get).max) - } else { - this.geq.orElse(that.geq) - } - - if (this.eq.isDefined && that.eq.isDefined && (this.eq != that.eq)) { - return Impossible() - } - val newEq = this.eq.orElse(that.eq) - - Constrained(eq=newEq, geq=newGeq, leq=newLeq, mof=newMof).minimize - case _ => ??? - } - } - - def minimize: Constraint = { - // Check range on LEQ/GEQ - val newEq = if (leq.isDefined && geq.isDefined) { - if (leq.get < geq.get) { - return Impossible() - } else if (leq.get == geq.get) { - if (eq.isDefined && (eq.get != leq.get)) { - return Impossible() - } - leq - } else { - eq - } - } else { - eq - } - - // Check multiples - if (eq.isDefined && mof.isDefined && (eq.get % mof.get != BigDecimal(0))) { - return Impossible() - } - - if (eq.isDefined) { - if (leq.isDefined) { - if (eq.get > leq.get) { - return Impossible() - } - } - if (geq.isDefined) { - if (eq.get < geq.get) { - return Impossible() - } - } - } - // TODO check if there exists a multiple in range - this.copy(eq=newEq) - } - - def +(that: Constraint): Constraint = { - that match { - case that: Unconstrained => this - case that: Impossible => that - case that: Constrained => - - // Combine raw constraints - val newMof = if (this.mof == that.mof) { - this.mof - } else { - None - } - - val newLeq = if (this.leq.isDefined && that.eq.isDefined) { - Some(this.leq.get + that.eq.get) - } else if (this.eq.isDefined && that.leq.isDefined) { - Some(this.eq.get + that.leq.get) - } else if (this.leq.isDefined && that.leq.isDefined) { - Some(this.leq.get + that.leq.get) - } else { - None - } - - val newGeq = if (this.geq.isDefined && that.eq.isDefined) { - Some(this.geq.get + that.eq.get) - } else if (this.eq.isDefined && that.geq.isDefined) { - Some(this.eq.get + that.geq.get) - } else if (this.geq.isDefined && that.geq.isDefined) { - Some(this.geq.get + that.geq.get) - } else { - None - } - - val newEq = if (this.eq.isDefined && that.eq.isDefined) { - Some(this.eq.get + that.eq.get) - } else { - None - } - - Constrained(eq=newEq, geq=newGeq, leq=newLeq, mof=newMof).minimize - case _ => ??? - } - } - - def *(that: BigDecimal): Constraint = Constrained( - eq = this.eq.map(_ * that), - geq = this.geq.map(_ * that), - leq = this.leq.map(_ * that), - mof = this.mof.map(_ * that) - ) - - def /(that: BigDecimal): Constraint = Constrained( - eq = this.eq.map(_ / that), - geq = this.geq.map(_ / that), - leq = this.leq.map(_ / that), - mof = this.mof.map(_ / that) - ) - - def test(value: BigDecimal): Boolean = { - val eqTest = this.eq.map(_ == value).getOrElse(true) - val geqTest = this.geq.map(_ <= value).getOrElse(true) - val leqTest = this.leq.map(_ >= value).getOrElse(true) - val mofTest = this.mof.map(x => (value % x) == 0).getOrElse(true) - return eqTest && geqTest && leqTest && mofTest - } -} - -object EqualTo { - def apply(value: BigDecimal) = Constrained(Some(value), None, None, None) -} - -object GreaterThanOrEqualTo { - def apply(value: BigDecimal) = Constrained(None, Some(value), None, None) -} - -object LessThanOrEqualTo { - def apply(value: BigDecimal) = Constrained(None, None, Some(value), None) -} - -object MultipleOf { - def apply(value: BigDecimal) = Constrained(None, None, None, Some(value)) -} - -case class Constraints( - width: Constraint = Unconstrained(), - height: Constraint = Unconstrained(), - area: Constraint = Unconstrained(), - aspectRatio: Constraint = Unconstrained() -) { - def test(widthValue: BigDecimal, heightValue: BigDecimal): Boolean = { - val widthTest = width.test(widthValue) - val heightTest = height.test(heightValue) - val areaTest = area.test(widthValue*heightValue) - val arTest = aspectRatio.test(heightValue/widthValue) - widthTest && heightTest && areaTest && arTest - } - - def weightXY(xWeight: BigDecimal, yWeight: BigDecimal): Constraints = Constraints( - width = this.width * xWeight, - height = this.height * yWeight, - area = this.area * (xWeight * yWeight), - aspectRatio = this.aspectRatio * (yWeight / xWeight) - ) - - def resolveMinDimensions(): (BigDecimal, BigDecimal) = resolveMinDimensions(BigDecimal(0), BigDecimal(0)) - - def resolveMinDimensions(defaultWidth: BigDecimal, defaultHeight: BigDecimal): (BigDecimal, BigDecimal) = { - if (this.aspectRatio.isConstrained) { - if (this.area.isConstrained) { - // AspectRatio with Area - if (this.width == Unconstrained() && this.height == Unconstrained()) { - val heightConstraint = BigDecimal(sqrt((this.area.resolveMin * this.aspectRatio.resolveMin).doubleValue)) // TODO clean up rounding - (this.area.resolveMin / heightConstraint, heightConstraint) - } else { - // TODO resolve 3- or 4-way constraint (this is wrong) - ??? - } - } else { - // AspectRatio with no Area - // Use defaultWidth (TODO make this an option?) - if (this.width == Unconstrained() && this.height == Unconstrained()) { - (defaultWidth, this.aspectRatio.resolveMin * defaultWidth) - } else if (this.height == Unconstrained()) { - (this.height.resolveMin / this.aspectRatio.resolveMin, this.height.resolveMin) - } else if (this.width == Unconstrained()) { - (this.width.resolveMin, this.aspectRatio.resolveMin * this.width.resolveMin) - } else { - // TODO resolve 3-way constraint - ??? - } - } - } else { - if (this.area.isConstrained) { - // Area with no AspectRatio - // Use defaultWidth (TODO make this an option?) - if (this.width == Unconstrained() && this.height == Unconstrained()) { - (defaultWidth, this.area.resolveMin / defaultWidth) - } else if (this.height == Unconstrained()) { - (this.width.resolveMin, this.area.resolveMin / this.width.resolveMin) - } else if (this.width == Unconstrained()) { - (this.area.resolveMin / this.height.resolveMin, this.height.resolveMin) - } else { - // TODO resolve 3-way constraint (this is wrong) - val widthReq = Seq(this.width.resolveMin, this.area.resolveMin / this.height.resolveMin).max - val heightReq = Seq(this.width.resolveMin, this.area.resolveMin / this.width.resolveMin).max - (widthReq, heightReq) - } - } else { - // No Area or AspectRatio - val widthConstraint = this.width match { - case x: Unconstrained => defaultWidth - case x => x.resolveMin - } - val heightConstraint = this.height match { - case x: Unconstrained => defaultHeight - case x => x.resolveMin - } - (widthConstraint, heightConstraint) - } - } - } - -} - -object Constraints { - - def sized(w: BigDecimal, h: BigDecimal): Constraints = { - Constraints( - EqualTo(w), - EqualTo(h), - Unconstrained(), - Unconstrained() - ) - } - -} - -sealed trait PlacementAnchor - -// TODO use this and convert to Enum -class LowerLeft extends PlacementAnchor -class LowerMiddle extends PlacementAnchor -class LowerRight extends PlacementAnchor -class CenterLeft extends PlacementAnchor -class CenterMiddle extends PlacementAnchor -class CenterRight extends PlacementAnchor -class UpperLeft extends PlacementAnchor -class UpperMiddle extends PlacementAnchor -class UpperRight extends PlacementAnchor - -object LowerLeft { def apply() = new LowerLeft } -object LowerMiddle { def apply() = new LowerMiddle } -object LowerRight { def apply() = new LowerRight } -object CenterLeft { def apply() = new CenterLeft } -object CenterMiddle { def apply() = new CenterMiddle } -object CenterRight { def apply() = new CenterRight } -object UpperLeft { def apply() = new UpperLeft } -object UpperMiddle { def apply() = new UpperMiddle } -object UpperRight { def apply() = new UpperRight } - diff --git a/floorplan/src/main/scala/barstools/floorplan/FloorplanSerialization.scala b/floorplan/src/main/scala/barstools/floorplan/FloorplanSerialization.scala deleted file mode 100644 index ab97f056..00000000 --- a/floorplan/src/main/scala/barstools/floorplan/FloorplanSerialization.scala +++ /dev/null @@ -1,39 +0,0 @@ -// See LICENSE for license details -package barstools.floorplan - -import org.json4s._ -import org.json4s.native.Serialization.{read, write, writePretty} -import org.json4s.ext.EnumNameSerializer -import scala.reflect.runtime.universe.typeOf - -object FloorplanSerialization { - - // Because Element is sealed, all of its subclasses are known at compile time, so we can construct type hints for them - private val typeHintClasses = Seq( - typeOf[Element], - typeOf[Unit], - typeOf[PlacementAnchor], - typeOf[Constraint], - ).map(_.typeSymbol.asClass.knownDirectSubclasses.toList).reduce(_ ++ _) - - val formats = (new DefaultFormats { - override val typeHintFieldName = "class" - override val typeHints = FullTypeHints(typeHintClasses map { x => Class.forName(x.fullName) }) - } + new EnumNameSerializer(Orientation)) - - def serialize[T <: Element](elt: T): String = write(elt)(formats) - - def deserialize(str: String): Element = { - // Not sure why the implicit doesn't work at the original definition, but the compiler complains - implicit val formats = FloorplanSerialization.formats - read[Element](str) - } - - def serializeState(state: FloorplanState): String = writePretty(state)(formats) - - def deserializeState(str: String): FloorplanState = { - implicit val formats = FloorplanSerialization.formats - read[FloorplanState](str) - } - -} diff --git a/floorplan/src/main/scala/barstools/floorplan/FloorplanState.scala b/floorplan/src/main/scala/barstools/floorplan/FloorplanState.scala deleted file mode 100644 index b2a5bac6..00000000 --- a/floorplan/src/main/scala/barstools/floorplan/FloorplanState.scala +++ /dev/null @@ -1,50 +0,0 @@ -// See LICENSE for license details -package barstools.floorplan - -import barstools.floorplan.hammer.HammerIR -import java.io.{File, FileWriter} - -final case class FloorplanRecord(scope: String, inst: Option[String], ofModule: Option[String], element: Element) { - def fullPath = scope + inst.map(x => if (x.startsWith("/")) x else "/" + x).getOrElse("") -} - -final case class FloorplanState(records: Seq[FloorplanRecord], level: Int) - -object FloorplanState { - - def fromSeq(seq: Seq[FloorplanRecord]): FloorplanState = FloorplanState(seq, (Seq(IRLevel.max) ++ seq.map(_.element.level)).max) - - def serialize(state: FloorplanState): String = FloorplanSerialization.serializeState(state) - def serialize(seq: Seq[FloorplanRecord]): String = serialize(fromSeq(seq)) - - def deserialize(str: String): FloorplanState = FloorplanSerialization.deserializeState(str) - - def fromFile(file: String): FloorplanState = { - val source = scala.io.Source.fromFile(file) - val fpState = deserialize(source.getLines.mkString("\n")) - source.close() - fpState - } - - def fromFiles(files: Seq[String]): FloorplanState = { - assert(files.length == 1, "FIXME combine multiple states into one") - fromFile(files(0)) - } - - def toFile(file: String, fmt: OutputFormat, state: FloorplanState) { - val writer = new FileWriter(new File(file)) - fmt match { - case OutputFormat.HammerIR => writer.write(HammerIR.serialize(HammerIR.fromFloorplanState(state))) - case OutputFormat.FloorplanIR => writer.write(FloorplanState.serialize(state)) - } - writer.close() - } -} - -sealed trait OutputFormat - -object OutputFormat { - case object HammerIR extends OutputFormat - case object FloorplanIR extends OutputFormat -} - diff --git a/floorplan/src/main/scala/barstools/floorplan/IR.scala b/floorplan/src/main/scala/barstools/floorplan/IR.scala deleted file mode 100644 index 9a50b889..00000000 --- a/floorplan/src/main/scala/barstools/floorplan/IR.scala +++ /dev/null @@ -1,449 +0,0 @@ -// See LICENSE for license details -package barstools.floorplan - -import scala.math.{BigInt, BigDecimal} - -// TODO Make some of this stuff private -// TODO make level an enum -// TODO add versioning to the FPIR file -// TODO clean up comment syntax, add scaladoc - -////////////////////////////////////////////// Base classes - -sealed trait Element { - def name: String - def level: Int - def serialize = FloorplanSerialization.serialize(this) - - def flatIndexOf(s: String): Int - - def mapNames(m: (String) => String): Element - - def toConstraints: Constraints -} - -sealed trait Constrainable extends Element { - def applyConstraints(c: Constraints): Element -} - -sealed trait HasFixedDimensions extends Constrainable { - def width: BigDecimal - def height: BigDecimal - def area: BigDecimal = width*height - - def testConstraints(c: Constraints): Boolean = { - return c.test(width, height) - } - - final def applyConstraints(c: Constraints): Element = { - if (this.testConstraints(c)) { - this - } else { - throw new Exception("Impossible constraints applied to previously sized element") - } - } -} - -sealed trait HasPlacement extends Element { - def x: BigDecimal - def y: BigDecimal -} - -sealed abstract class ElementWithParent extends Element { - def parent: String -} - -sealed abstract class Primitive extends ElementWithParent { - def flatIndexOf(s: String): Int = -1 -} - -sealed abstract class Group extends ElementWithParent { - def elements: Seq[String] - - def flatIndexOf(s: String): Int = elements.indexOf(Some(s)) -} - -sealed abstract class Top extends Element - -////////////////////////////////////////////// Hierarchical barrier - -private[floorplan] final case class HierarchicalBarrier( - name: String, - parent: String -) extends Primitive with AbstractRectLike { - final def level = 3 - def mapNames(m: (String) => String): Element = this.copy(name = m(name), parent = m(parent)) -} - -////////////////////////////////////////////// Rectangular things - -sealed trait AbstractRectLike extends Element { - def toConstraints: Constraints = Constraints() -} - -sealed trait ConstrainedRectLike extends Constrainable { - def width: Constraint - def height: Constraint - def area: Constraint - def aspectRatio: Constraint - - def toConstraints: Constraints = Constraints(width, height, area, aspectRatio) -} - -sealed trait SizedRectLike extends HasFixedDimensions { - def toConstraints: Constraints = Constraints.sized(width, height) -} - -sealed trait PlacedRectLike extends SizedRectLike with HasPlacement - -object IRLevel { - def max = 4 -} - -sealed abstract class AbstractRectPrimitive extends Primitive with AbstractRectLike { - final def level = 4 -} - -sealed abstract class ConstrainedRectPrimitive extends Primitive with ConstrainedRectLike { - final def level = 2 -} - -sealed abstract class SizedRectPrimitive extends Primitive with SizedRectLike { - final def level = 1 -} - -sealed abstract class PlacedRectPrimitive extends Primitive with PlacedRectLike { - final def level = 0 -} - -private[floorplan] final case class ConstrainedSpacerRect( - name: String, - parent: String, - width: Constraint = Unconstrained(), - height: Constraint = Unconstrained(), - area: Constraint = Unconstrained(), - aspectRatio: Constraint = Unconstrained() -) extends ConstrainedRectPrimitive { - def mapNames(m: (String) => String): Element = this.copy(name = m(name), parent = m(parent)) - def applyConstraints(c: Constraints): Element = this.copy( - width = width.and(c.width), - height = height.and(c.height), - area = area.and(c.area), - aspectRatio = aspectRatio.and(c.aspectRatio) - ) -} - -private[floorplan] final case class SizedSpacerRect( - name: String, - parent: String, - width: BigDecimal, - height: BigDecimal -) extends SizedRectPrimitive { - def mapNames(m: (String) => String): Element = this.copy(name = m(name), parent = m(parent)) -} - -// No PlacedSpacerRect exists because they're only for spacing - -/////////////////////////// Hierarchical top modules - -case class Margins(left: BigDecimal, right: BigDecimal, top: BigDecimal, bottom: BigDecimal) - -object Margins { - def empty = Margins(BigDecimal(0), BigDecimal(0), BigDecimal(0), BigDecimal(0)) -} - -private[floorplan] final case class ConstrainedLogicRect( - name: String, - parent: String, - width: Constraint, - height: Constraint, - area: Constraint, - aspectRatio: Constraint, - hardBoundary: Boolean -) extends ConstrainedRectPrimitive { - def mapNames(m: (String) => String): Element = this.copy(name = m(name), parent = m(parent)) - def applyConstraints(c: Constraints): Element = this.copy( - width = width.and(c.width), - height = height.and(c.height), - area = area.and(c.area), - aspectRatio = aspectRatio.and(c.aspectRatio) - ) -} - -private[floorplan] final case class SizedLogicRect( - name: String, - parent: String, - width: BigDecimal, - height: BigDecimal, - hardBoundary: Boolean -) extends SizedRectPrimitive { - def mapNames(m: (String) => String): Element = this.copy(name = m(name), parent = m(parent)) -} - -private[floorplan] final case class PlacedLogicRect( - name: String, - parent: String, - x: BigDecimal, - y: BigDecimal, - width: BigDecimal, - height: BigDecimal, - hardBoundary: Boolean -) extends PlacedRectPrimitive { - def mapNames(m: (String) => String): Element = this.copy(name = m(name), parent = m(parent)) -} - - -private[floorplan] final case class ConstrainedHierarchicalTop( - name: String, - topGroup: String, - width: Constraint, - height: Constraint, - area: Constraint, - aspectRatio: Constraint, - margins: Margins, - hardBoundary: Boolean -) extends Top with ConstrainedRectLike { - final def level = 2 - def mapNames(m: (String) => String): Element = this.copy(name = m(name), topGroup = m(topGroup)) - def flatIndexOf(s: String): Int = if (topGroup == s) 0 else -1 - def applyConstraints(c: Constraints): Element = this.copy( - width = width.and(c.width), - height = height.and(c.height), - area = area.and(c.area), - aspectRatio = aspectRatio.and(c.aspectRatio) - ) -} - -private[floorplan] final case class SizedHierarchicalTop( - name: String, - topGroup: String, - width: BigDecimal, - height: BigDecimal, - margins: Margins, - hardBoundary: Boolean -) extends Top with SizedRectLike { - final def level = 1 - def mapNames(m: (String) => String): Element = this.copy(name = m(name), topGroup = m(topGroup)) - def flatIndexOf(s: String): Int = if (topGroup == s) 0 else -1 -} - -private[floorplan] final case class PlacedHierarchicalTop( - name: String, - elements: Seq[String], - x: BigDecimal, - y: BigDecimal, - width: BigDecimal, - height: BigDecimal, - margins: Margins, - hardBoundary: Boolean -) extends Top with PlacedRectLike { - final def level = 0 - def mapNames(m: (String) => String): Element = this.copy(name = m(name), elements.map(m)) - def flatIndexOf(s: String): Int = elements.indexOf(s) -} - -////////////////////////////////////////////// Aggregate (Group) things - -sealed abstract class Grid extends Group { - def xDim: Int - def yDim: Int - def elements: Seq[String] - - assert(xDim > 0, "X dimension of grid must be positive") - assert(yDim > 0, "Y dimension of grid must be positive") - assert(elements.length == (xDim*yDim), s"Grid {name} has incorrect number of elements ({elements.length})") - - def toIdx(x: Int, y: Int): Int = xDim*y + x - def fromIdx(i: Int): (Int, Int) = (i % xDim, i / xDim) - - def get(x: Int, y: Int) = elements(toIdx(x, y)) - - def indexOf(s: String): Option[(Int, Int)] = { - val idx = elements.indexOf(Some(s)) - if (idx == -1) None else Some(fromIdx(idx)) - } -} - -private[floorplan] final case class ConstrainedWeightedGrid( - name: String, - parent: String, - xDim: Int, - yDim: Int, - elements: Seq[String], - xWeights: Seq[BigDecimal], - yWeights: Seq[BigDecimal], - width: Constraint, - height: Constraint, - area: Constraint, - aspectRatio: Constraint -) extends Grid with ConstrainedRectLike { - def level = 2 - def mapNames(m: (String) => String): Element = { - this.copy( - name = m(name), - parent = m(parent), - elements = elements.map(m) - ) - } - def applyConstraints(c: Constraints): Element = this.copy( - width = width.and(c.width), - height = height.and(c.height), - area = area.and(c.area), - aspectRatio = aspectRatio.and(c.aspectRatio) - ) - - def getXWeight(x: Int): BigDecimal = xWeights(x) / xWeights.sum - def getYWeight(y: Int): BigDecimal = yWeights(y) / yWeights.sum -} - -private[floorplan] final case class ConstrainedElasticGrid( - name: String, - parent: String, - xDim: Int, - yDim: Int, - elements: Seq[String], - width: Constraint, - height: Constraint, - area: Constraint, - aspectRatio: Constraint -) extends Grid with ConstrainedRectLike { - def level = 2 - def mapNames(m: (String) => String): Element = { - this.copy( - name = m(name), - parent = m(parent), - elements = elements.map(m) - ) - } - def applyConstraints(c: Constraints): Element = this.copy( - width = width.and(c.width), - height = height.and(c.height), - area = area.and(c.area), - aspectRatio = aspectRatio.and(c.aspectRatio) - ) -} - -private[floorplan] final case class SizedGrid( - name: String, - parent: String, - xDim: Int, - yDim: Int, - elements: Seq[String], - widths: Seq[BigDecimal], - heights: Seq[BigDecimal] -) extends Grid with SizedRectLike { - def level = 1 - def mapNames(m: (String) => String): Element = { - this.copy( - name = m(name), - parent = m(parent), - elements = elements.map(m) - ) - } - def width: BigDecimal = widths.sum - def height: BigDecimal = heights.sum -} - - -////////////////////////////////////////////// MemElements and Macros - -// Reference to a MemElement -private[floorplan] final case class MemElement( - name: String, - parent: String -) extends AbstractRectPrimitive { - def mapNames(m: (String) => String): Element = this.copy(name = m(name), parent = m(parent)) -} - -// Container for MemElements -private[floorplan] final case class MemElementArray( - name: String, - parent: String, - elements: Seq[String], - width: Constraint = Unconstrained(), - height: Constraint = Unconstrained(), - area: Constraint = Unconstrained(), - aspectRatio: Constraint = Unconstrained() -) extends Group with ConstrainedRectLike { - def level = 4 - def mapNames(m: (String) => String): Element = { - this.copy( - name = m(name), - parent = m(parent), - elements = elements.map(m) - ) - } - def applyConstraints(c: Constraints): Element = this.copy( - width = width.and(c.width), - height = height.and(c.height), - area = area.and(c.area), - aspectRatio = aspectRatio.and(c.aspectRatio) - ) -} - -// Container for MemElements that have been converted to Macros -// This may be unnecessary, but the purpose of this is to let the floorplan -// tool treat memory macros differently than generic macros, since they -// are more commonly arrayed -private[floorplan] final case class MemMacroArray( - name: String, - parent: String, - elements: Seq[String], - width: Constraint = Unconstrained(), - height: Constraint = Unconstrained(), - area: Constraint = Unconstrained(), - aspectRatio: Constraint = Unconstrained() -) extends Group with ConstrainedRectLike { - def level = 2 - def mapNames(m: (String) => String): Element = { - this.copy( - name = m(name), - parent = m(parent), - elements = elements.map(m) - ) - } - def applyConstraints(c: Constraints): Element = this.copy( - width = width.and(c.width), - height = height.and(c.height), - area = area.and(c.area), - aspectRatio = aspectRatio.and(c.aspectRatio) - ) -} - -// Reference to a macro blackbox with unknown dimensions -// Do not use for SyncReadMem objects; use MemElement instead -private[floorplan] final case class AbstractMacro ( - name: String, - parent: String -) extends AbstractRectPrimitive { - def mapNames(m: (String) => String): Element = this.copy(name = m(name), parent = m(parent)) -} - -// Reference to a macro blackbox that has known dimensions -private[floorplan] final case class SizedMacro ( - name: String, - parent: String, - orientation: Orientation.Value, - width: BigDecimal, - height: BigDecimal -) extends SizedRectPrimitive { - def mapNames(m: (String) => String): Element = this.copy(name = m(name), parent = m(parent)) -} - -// Reference to a macro blackbox that has known dimensions and has been placed -private[floorplan] final case class PlacedMacro ( - name: String, - parent: String, - orientation: Orientation.Value, - x: BigDecimal, - y: BigDecimal, - width: BigDecimal, - height: BigDecimal -) extends PlacedRectPrimitive { - def mapNames(m: (String) => String): Element = this.copy(name = m(name), parent = m(parent)) -} - -object Orientation extends Enumeration { - val r0, r90, r180, r270, mx, mx90, my, my90 = Value -} - diff --git a/floorplan/src/main/scala/barstools/floorplan/chisel/API.scala b/floorplan/src/main/scala/barstools/floorplan/chisel/API.scala deleted file mode 100644 index a701a73c..00000000 --- a/floorplan/src/main/scala/barstools/floorplan/chisel/API.scala +++ /dev/null @@ -1,494 +0,0 @@ -// See LICENSE for license details -package barstools.floorplan.chisel - -import chisel3.{RawModule, MemBase, Data} - -import firrtl.annotations.{ReferenceTarget, InstanceTarget, ModuleTarget, Target, Annotation} - -import barstools.floorplan._ -import barstools.floorplan.firrtl.{FloorplanAnnotation, MemFloorplanAnnotation, InstanceFloorplanAnnotation, NoReferenceFloorplanAnnotation} -import scala.collection.mutable.{ArraySeq, ArrayBuffer, HashMap, Set, HashSet} -import scala.math.{BigInt, BigDecimal} - -final case class ChiselFloorplanException(message: String) extends Exception(message: String) - -final class ChiselFloorplanContext private[chisel] (val scope: Target, val topElement: ChiselHierarchicalTop) { - - private[chisel] val elementBuf = new ArrayBuffer[ChiselElement]() - - elementBuf.append(topElement) - - private def addElement[T <: ChiselElement](e: T): T = { - FloorplanDatabase.register(scope, e) - elementBuf.append(e) - e - } - - def commit(): Seq[ChiselElement] = { - elementBuf.toSeq.collect { - case g: ChiselGroupElement => g.commit() - } - // Note that this is mutable and is different than above! - elementBuf.toSeq - } - - def createRect[T <: RawModule](module: T, - width: Constraint = Unconstrained(), - height: Constraint = Unconstrained(), - area: Constraint = Unconstrained(), - aspectRatio: Constraint = Unconstrained(), - hardBoundary: Boolean = true - ): ChiselLogicRect = { - val inst = module.toAbsoluteTarget.asInstanceOf[InstanceTarget] - val name = FloorplanDatabase.getUnusedName(scope, inst) - val elt = new ChiselLogicRect(scope, name, inst, width, height, area, aspectRatio, hardBoundary) - addElement(elt) - } - - def createSpacer( - name: Option[String] = None, - width: Constraint = Unconstrained(), - height: Constraint = Unconstrained(), - area: Constraint = Unconstrained(), - aspectRatio: Constraint = Unconstrained() - ): ChiselSpacerRect = { - val nameStr = FloorplanDatabase.getUnusedName(scope, name) - val elt = new ChiselSpacerRect(scope, nameStr, width, height, area, aspectRatio) - addElement(elt) - } - - def createElasticGrid(xDim: Int, yDim: Int, name: Option[String] = None): ChiselElasticGrid = { - val nameStr = FloorplanDatabase.getUnusedName(scope, name) - val elt = new ChiselElasticGrid(scope, nameStr, this, xDim, yDim) - addElement(elt) - } - - def createElasticArray(dim: Int, dir: Direction, name: Option[String]): ChiselElasticArray = { - val nameStr = FloorplanDatabase.getUnusedName(scope, name) - val elt = new ChiselElasticArray(scope, nameStr, this, dim, dir) - addElement(elt) - } - - def createElasticArray(dim: Int, name: Option[String]): ChiselElasticArray = createElasticArray(dim, Direction.Horizontal, name) - def createElasticArray(dim: Int, dir: Direction): ChiselElasticArray = createElasticArray(dim, dir, None) - def createElasticArray(dim: Int): ChiselElasticArray = createElasticArray(dim, Direction.Horizontal, None) - def createElasticArray(elts: Seq[ChiselElement], dir: Direction = Direction.Horizontal, name: Option[String] = None): ChiselElasticArray = { - val ary = createElasticArray(elts.length, dir, name) - elts.zipWithIndex.foreach { case (e, i) => ary.placeAt(i, e) } - ary - } - - def createMemArray(name: Option[String] = None, aspectRatio: Option[BigDecimal] = Some(BigDecimal(1))): ChiselMemArray = { - val nameStr = FloorplanDatabase.getUnusedName(scope, name) - val elt = new ChiselMemArray(scope, nameStr, aspectRatio, this) - addElement(elt) - } - - def addHier[T <: RawModule](module: T): ChiselHierarchicalBarrier = { - val inst = module.toAbsoluteTarget.asInstanceOf[InstanceTarget] - val name = FloorplanDatabase.getUnusedName(scope, inst) - val elt = new ChiselHierarchicalBarrier(scope, name, inst) - addElement(elt) - } - - private[chisel] def addMem[T <: Data](mem: MemBase[T]): ChiselMem = { - val ref = mem.toAbsoluteTarget - val name = FloorplanDatabase.getUnusedName(scope, ref) - val elt = new ChiselMem(scope, name, ref) - addElement(elt) - } - - def setTopGroup[T <: ChiselGroupElement](g: T): T = { - topElement.setTopGroup(g) - g - } - -} - -object Floorplan { - - - def apply[T <: RawModule](module: T): ChiselFloorplanContext = apply( - module = module, - width = Unconstrained(), - height = Unconstrained(), - area = Unconstrained(), - aspectRatio = Unconstrained(), - hardBoundary = true, - margins = Margins.empty - ) - - def apply[T <: RawModule](module: T, - width: Constraint, - height: Constraint, - area: Constraint, - aspectRatio: Constraint, - hardBoundary: Boolean, - margins: Margins - ): ChiselFloorplanContext = { - val scope: Target = module.toAbsoluteTarget - val modName = scope match { - case r: InstanceTarget => r.ofModule - case r: ModuleTarget => r.module - case _ => ??? - } - val name = FloorplanDatabase.getUnusedName(scope, modName) - val elt = new ChiselHierarchicalTop(scope, name, width, height, area, aspectRatio, margins, hardBoundary) - FloorplanDatabase.register(scope, elt) - new ChiselFloorplanContext(scope, elt) - } - - def apply[T <: RawModule](module: T, - width: Double, - height: Double - ): ChiselFloorplanContext = apply( - module = module, - width = EqualTo(BigDecimal(width)), - height = EqualTo(BigDecimal(height)), - area = Unconstrained(), - aspectRatio = Unconstrained(), - hardBoundary = true, - margins = Margins.empty - ) - - def apply[T <: RawModule](module: T, - width: String, - height: String - ): ChiselFloorplanContext = apply( - module = module, - width = EqualTo(BigDecimal(width)), - height = EqualTo(BigDecimal(height)), - area = Unconstrained(), - aspectRatio = Unconstrained(), - hardBoundary = true, - margins = Margins.empty - ) - -} - -private[chisel] object FloorplanDatabase { - - private val nameMap = new HashMap[Target, Set[String]]() - private val elements = new HashSet[ChiselElement]() - - private def getSet(scope: Target) = nameMap.getOrElseUpdate(scope, new HashSet[String]) - - // TODO I'm not sure this is necessary anymore - def register(scope: Target, element: ChiselElement): Unit = { - val name = element.name - val set = getSet(scope) - if (set.contains(name)) { - throw new ChiselFloorplanException(s"Duplicate floorplan element registration ${name} for Target "+scope.toString+"!") - } - elements.add(element) - set.add(name) - } - - def getUnusedName(scope: Target): String = getUnusedName(scope, None) - - def getUnusedName(scope: Target, suggestion: Option[String]): String = getUnusedName(scope, suggestion.getOrElse("unnamed")) - - def getUnusedName(scope: Target, suggestion: String): String = { - val set = getSet(scope) - var id = 0 - // This is slow and bad, but hopefully rare - while (set.contains(suggestion + s"_${id}")) { id = id + 1 } - suggestion + s"_${id}" - } - - def getUnusedName(scope: Target, inst: Target): String = { - val instName = inst match { - case t: InstanceTarget => t.instance - case t: ModuleTarget => t.module - case t: ReferenceTarget => t.ref - case _ => ??? - } - getUnusedName(scope, instName) - } - -} - -object FloorplanUnits { - - // TODO do we make this an annotation? - private final case class FloorplanUnitsException(message: String) extends Exception(message) - - // This corresponds to the scaling factor between user input and database units, such that the - // smallest database unit will correspond to 1. Currently only supports units <= 1. - // e.g. - // - // db unit | unit - // 0.001 um | 1000 - // 0.005 um | 200 - // 1 um | 1 - // 10 um | error - private var unit = Option.empty[Long] - - def set(x: Double) { - if (x > 1) { - throw new FloorplanUnitsException("Cannot set base unit to a value > 1.") - } - if (unit.nonEmpty) { - throw new FloorplanUnitsException("Cannot set units more than once!") - } - unit = Some(scala.math.round(1/x)) - } - - def get = unit - - def area(x: Double): BigDecimal = { - unit.map { u => - BigDecimal(scala.math.round(u*x*x)) - }.getOrElse { - throw new FloorplanUnitsException("Cannot create floorplan with concrete units- units have not been set! Call FloorplanUnits.set first.") - } - } - - def length(x: Double): BigDecimal = { - unit.map { u => - BigDecimal(scala.math.round(u*x)) - }.getOrElse { - throw new FloorplanUnitsException("Cannot create floorplan with concrete units- units have not been set! Call FloorplanUnits.set first.") - } - } - -} - -trait Direction { - def ifH[T](h: T, v: T): T = this match { - case Direction.Horizontal => h - case Direction.Vertical => v - } - def ifV[T](v: T, h: T): T = ifH(h, v) -} - -object Direction { - case object Vertical extends Direction - case object Horizontal extends Direction -} - -sealed abstract class ChiselElement(val scope: Target, val name: String) { - protected def generateElement(): Element - private[chisel] def getFloorplanAnnotations(): Seq[FloorplanAnnotation] - def getAnnotations(): Seq[Annotation] = getFloorplanAnnotations() - private var parent = Option.empty[CanBeParent] - private[chisel] def setParent(p: CanBeParent) { - assert(!this.parent.isDefined, "Element cannot have multiple parents") - this.parent = Some(p) - } - protected def parentName: String = { - assert(this.parent.isDefined, "Parent is not defined for this element") - this.parent.get.name - } -} - -sealed trait CanBeParent { - this: ChiselElement => - def name: String -} - -sealed abstract class ChiselNoReferenceElement(scope: Target, name: String) extends ChiselElement(scope, name) { - private[chisel] def getFloorplanAnnotations() = Seq(NoReferenceFloorplanAnnotation(scope, generateElement())) -} - -sealed abstract class ChiselInstanceElement(scope: Target, name: String, val instance: Target) extends ChiselElement(scope, name) { - private[chisel] def getFloorplanAnnotations() = Seq(InstanceFloorplanAnnotation(Seq(Seq(scope), Seq(instance)), generateElement())) -} - -sealed abstract class ChiselMemElement(scope: Target, name: String, val reference: ReferenceTarget) extends ChiselElement(scope, name) { - private[chisel] def getFloorplanAnnotations() = Seq(MemFloorplanAnnotation(Seq(Seq(scope), Seq(reference)), generateElement())) -} - -sealed abstract class ChiselGroupElement(scope: Target, name: String, val context: ChiselFloorplanContext) - extends ChiselElement(scope, name) with CanBeParent { - - protected def initialSize: Int - protected val elements = ArrayBuffer.fill(initialSize)(Option.empty[ChiselElement]) - protected var isCommitted = false - - protected def generateGroupElement(names: Seq[String]): Group - - private[chisel] def commit() { - // in scala 2.13 this is much cleaner: - //elements.mapInPlace(x => Some(x.getOrElse(context.createSpacer()))) - elements.zipWithIndex.foreach { case (elt, idx) => - if (elt.isEmpty) { - this._placeAt(idx, context.createSpacer()) - } - } - isCommitted = true - } - - protected def generateElement(): Group = { - assert(isCommitted) - generateGroupElement(elements.map(_.get.name)) - } - - private[chisel] def _placeAt[T <: ChiselElement](idx: Int, e: T): T = { - assert(!isCommitted, "Cannot add elements after committing") - // This is only supported in scala 2.13 - //elements.padToInPlace(idx+1, None) - for (i <- elements.length until (idx+1)) elements += None - assert(!elements(idx).isDefined, "Already added at this location") - elements(idx) = Some(e) - e.setParent(this) - e - } - - private[chisel] def getFloorplanAnnotations() = Seq(NoReferenceFloorplanAnnotation(scope, generateElement())) -} - -abstract class ChiselArrayElement(scope: Target, name: String, context: ChiselFloorplanContext) extends ChiselGroupElement(scope, name, context) { - - private[chisel] def addElement(e: ChiselElement) { - assert(!isCommitted, "Cannot add elements after committing") - elements += Some(e) - e.setParent(this) - } - -} - -abstract class ChiselGridElement(scope: Target, name: String, context: ChiselFloorplanContext, val xDim: Int, val yDim: Int) extends ChiselGroupElement(scope, name, context) { - - protected def initialSize = xDim * yDim - protected def toIdx(x: Int, y: Int): Int = xDim*y + x - - def placeAt[T <: ChiselElement](x: Int, y: Int, e: T): T = { - assert(x >= 0) - assert(y >= 0) - assert(x < xDim) - assert(y < yDim) - _placeAt(toIdx(x, y), e) - } - -} - -final class ChiselHierarchicalTop private[chisel] ( - scope: Target, - name: String, - val width: Constraint, - val height: Constraint, - val area: Constraint, - val aspectRatio: Constraint, - val margins: Margins, - val hardBoundary: Boolean -) extends ChiselNoReferenceElement(scope, name) with CanBeParent { - private var topGroup = Option.empty[ChiselGroupElement] - - private def topGroupName: String = { - assert(this.topGroup.isDefined, "HierarchicalTop needs a topGroup") - this.topGroup.get.name - } - - protected def generateElement(): Element = ConstrainedHierarchicalTop(name, topGroupName, width, height, area, aspectRatio, margins, hardBoundary) - - private[chisel] def setTopGroup(t: ChiselGroupElement) { - assert(!this.topGroup.isDefined, "Cannot set topGroup twice") - t.setParent(this) - this.topGroup = Some(t) - } -} - -final class ChiselHierarchicalBarrier private[chisel] ( - scope: Target, - name: String, - instance: InstanceTarget -) extends ChiselInstanceElement(scope, name, instance) { - protected def generateElement(): Element = HierarchicalBarrier(name, parentName) -} - -final class ChiselLogicRect private[chisel] ( - scope: Target, - name: String, - instance: InstanceTarget, - val width: Constraint, - val height: Constraint, - val area: Constraint, - val aspectRatio: Constraint, - val hardBoundary: Boolean -) extends ChiselInstanceElement(scope, name, instance) { - protected def generateElement(): Element = ConstrainedLogicRect(name, parentName, width, height, area, aspectRatio, hardBoundary) -} - -final class ChiselSpacerRect private[chisel] ( - scope: Target, - name: String, - val width: Constraint = Unconstrained(), - val height: Constraint = Unconstrained(), - val area: Constraint = Unconstrained(), - val aspectRatio: Constraint = Unconstrained() -) extends ChiselNoReferenceElement(scope, name) { - protected def generateElement(): Element = ConstrainedSpacerRect(name, parentName, width, height, area, aspectRatio) -} - -final class ChiselMem private[chisel] ( - scope: Target, - name: String, - reference: ReferenceTarget -) extends ChiselMemElement(scope, name, reference) { - protected def generateElement(): Element = MemElement(name, parentName) -} - -final class ChiselMemArray private[chisel] ( - scope: Target, - name: String, - aspectRatio: Option[BigDecimal], - context: ChiselFloorplanContext -) extends ChiselArrayElement(scope, name, context) { - protected def initialSize = 0 - protected def generateGroupElement(names: Seq[String]): Group = MemElementArray(name, parentName, names, Unconstrained(), Unconstrained(), Unconstrained(), Constrained(eq = aspectRatio)) - - def addMem[T <: Data](mem: MemBase[T]) = this.addElement(this.context.addMem(mem)) -} - -class ChiselElasticGrid private[chisel] ( - scope: Target, - name: String, - context: ChiselFloorplanContext, - xDim: Int, - yDim: Int -) extends ChiselGridElement(scope, name, context, xDim, yDim) { - final protected def generateGroupElement(names: Seq[String]): Group = ConstrainedElasticGrid( - name, - parentName, - xDim, - yDim, - names, - Unconstrained(), - Unconstrained(), - Unconstrained(), - Unconstrained()) -} - -class ChiselElasticArray private[chisel] ( - scope: Target, - name: String, - context: ChiselFloorplanContext, - dim: Int, - val dir: Direction -) extends ChiselElasticGrid(scope, name, context, dir.ifH(dim,1), dir.ifV(dim,1)) { - def placeAt[T <: ChiselElement](i: Int, e: T): T = super.placeAt(dir.ifH(i,0), dir.ifV(i,0), e) -} - -class ChiselWeightedGrid private[chisel] ( - scope: Target, - name: String, - context: ChiselFloorplanContext, - xDim: Int, - yDim: Int, - xWeights: Seq[BigDecimal], - yWeights: Seq[BigDecimal] -) extends ChiselGridElement(scope, name, context, xDim, yDim) { - final protected def generateGroupElement(names: Seq[String]): Group = ConstrainedWeightedGrid( - name, - parentName, - xDim, - yDim, - names, - xWeights, - yWeights, - Unconstrained(), - Unconstrained(), - Unconstrained(), - Unconstrained()) -} diff --git a/floorplan/src/main/scala/barstools/floorplan/chisel/FloorplanAspect.scala b/floorplan/src/main/scala/barstools/floorplan/chisel/FloorplanAspect.scala deleted file mode 100644 index 60952d4a..00000000 --- a/floorplan/src/main/scala/barstools/floorplan/chisel/FloorplanAspect.scala +++ /dev/null @@ -1,27 +0,0 @@ -// See LICENSE for license details -package barstools.floorplan.chisel - -import chisel3.{RawModule} -import chisel3.aop.{Aspect, Select} -import firrtl.{AnnotationSeq} - -abstract class FloorplanAspect[T <: RawModule](floorplans: FloorplanFunction) extends Aspect[T] { - - // Guarantee only one FloorplanAspect will be created - FloorplanAspect.setHasRun() - - final override def toAnnotation(top: T): AnnotationSeq = { - AnnotationSeq(Select.collectDeep(top)(floorplans).toList.flatten.flatMap(_.getAnnotations())) - } -} - -object FloorplanAspect { - private var hasRun = false - - private[chisel] def setHasRun() = { - if (hasRun) { - throw new Exception("Cannot run FloorplanAspect more than once; combine partial functions into a single FloorplanAspect instead.") - } - hasRun = true - } -} diff --git a/floorplan/src/main/scala/barstools/floorplan/chisel/package.scala b/floorplan/src/main/scala/barstools/floorplan/chisel/package.scala deleted file mode 100644 index 6415cc0c..00000000 --- a/floorplan/src/main/scala/barstools/floorplan/chisel/package.scala +++ /dev/null @@ -1,11 +0,0 @@ -// See LICENSE for license details -package barstools.floorplan - -package object chisel { - - import chisel3.experimental.{BaseModule} - - type FloorplanFunction = PartialFunction[BaseModule, Seq[ChiselElement]] - -} - diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/CalculatePlacementsPass.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/CalculatePlacementsPass.scala deleted file mode 100644 index b7028d5d..00000000 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/CalculatePlacementsPass.scala +++ /dev/null @@ -1,92 +0,0 @@ -// See LICENSE for license details -package barstools.floorplan.compiler - -import barstools.floorplan._ - -import scala.collection.mutable.{ArrayBuffer} - -class CalculatePlacementsPass(topMod: String) extends Pass { - def execute(state: FloorplanState): FloorplanState = { - val tree = new FloorplanTree(state, topMod) - - val top = tree.topNode - assert(top.record.element.isInstanceOf[SizedHierarchicalTop], "PlacedHierarchicalTop not yet supported pre-CalculatePlacementsPass. Use SizedHierarchicalTop instead.") - - val elementBuf = new ArrayBuffer[Element]() - - // Custom traversal TODO should this go in the FloorplanTree code somehow? - def traverse(n: FloorplanTreeNode, x: BigDecimal, y: BigDecimal, placeTopOpt: Option[FloorplanTreeNode]) { - // TODO this needs to be cleaned up elsewhere - val legalX = x.setScale(3, BigDecimal.RoundingMode.HALF_UP) - val legalY = y.setScale(3, BigDecimal.RoundingMode.HALF_UP) - n.record.element match { - case e: SizedHierarchicalTop => - assert(placeTopOpt.isEmpty) - // Note: we intentionally wait to add this until the end so we know all of the elements - val children = n.children - assert(children.length == 1) // should only be a topGroup here - children.foreach { cNode => traverse(cNode, x, y, Some(n)) } - n.replace(n.record.copy(element = PlacedHierarchicalTop( - e.name, - elementBuf.toSeq.map(_.name), - legalX, - legalY, - e.width, - e.height, - e.margins, - e.hardBoundary - ))) - case e: SizedGrid => - assert(placeTopOpt.isDefined) - // traverse down - val nodes: Seq[FloorplanTreeNode] = e.elements.map(name => tree.getNode(name)) - val children: Seq[SizedRectLike] = nodes.map(_.record.element.asInstanceOf[SizedRectLike]) - val widths: Seq[BigDecimal] = children.take(e.xDim).map(_.width).scanLeft(BigDecimal(0))(_+_).take(e.xDim) - val heights: Seq[BigDecimal] = children.grouped(e.xDim).map(_(0).height).toSeq.scanLeft(BigDecimal(0))(_+_).take(e.yDim) - - nodes.zipWithIndex.foreach { case (node, idx) => - val (iX, iY) = e.fromIdx(idx) - traverse(node, legalX + widths(iX), legalY + heights(iY), placeTopOpt) - } - - // delete it - n.delete() - case e: SizedLogicRect => - assert(placeTopOpt.isDefined) - n.replace(n.record.copy(element = PlacedLogicRect( - e.name, - e.parent, - legalX, - legalY, - e.width, - e.height, - e.hardBoundary - ))) - n.reparent(placeTopOpt.get) - elementBuf.append(n.record.element) - case e: SizedSpacerRect => - assert(placeTopOpt.isDefined) - // delete it - n.delete() - case e: SizedMacro => - assert(placeTopOpt.isDefined) - n.replace(n.record.copy(element = PlacedMacro( - e.name, - e.parent, - e.orientation, - legalX, - legalY, - e.width, - e.height - ))) - n.reparent(placeTopOpt.get) - elementBuf.append(n.record.element) - case e => ??? // Shouldn't get here - } - } - - traverse(top, BigDecimal(0), BigDecimal(0), None) - - tree.toState - } -} diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/ConstraintPropagationPass.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/ConstraintPropagationPass.scala deleted file mode 100644 index d5fc62c2..00000000 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/ConstraintPropagationPass.scala +++ /dev/null @@ -1,174 +0,0 @@ -// See LICENSE for license details -package barstools.floorplan.compiler - -import barstools.floorplan._ - -class ConstraintPropagationPass(val topMod: String) extends Pass { - def execute(state: FloorplanState): FloorplanState = { - val tree = new FloorplanTree(state, topMod) - - // Iterate a couple times (this is a hack FIXME) - val nIter = 3 - for (iter <- (0 until nIter)) { - // Top-down pass - tree.traverseMapPre { node => - val constraints: Constraints = node.parent.map(_.record.element match { - case e: ConstrainedHierarchicalTop => - e.toConstraints - case e: SizedHierarchicalTop => - Constraints() // These should be sized already - case e: PlacedHierarchicalTop => - Constraints() // These should be sized already - case e: ConstrainedWeightedGrid => - val (x, y) = e.indexOf(node.record.element.name).get - val xWeight = e.getXWeight(x) - val yWeight = e.getYWeight(y) - e.toConstraints.weightXY(xWeight, yWeight) - case e: ConstrainedElasticGrid => - val c = e.toConstraints - val widthConstraint = c.width match { - case x: Unconstrained => x - case x: Impossible => x - case x: Constrained => x.copy(eq = None, leq = x.eq.orElse(x.leq), geq = None, mof = None) - } - val heightConstraint = c.height match { - case x: Unconstrained => x - case x: Impossible => x - case x: Constrained => x.copy(eq = None, leq = x.eq.orElse(x.leq), geq = None, mof = None) - } - val areaConstraint = c.area match { - case x: Unconstrained => x - case x: Impossible => x - case x: Constrained => x.copy(eq = None, leq = x.eq.orElse(x.leq), geq = None, mof = None) - } - Constraints(width = widthConstraint, height = heightConstraint, area = areaConstraint, aspectRatio = Unconstrained()) - case e: SizedGrid => - val (x, y) = e.indexOf(node.record.element.name).get - Constraints.sized(e.widths(x), e.heights(y)) - case e: MemMacroArray => - Constraints() // These *should* be hard macros at this point, so no need to constrain them. Maybe aspect ratio? - case _ => ??? // Many types can never be parents and shouldn't get here - }).getOrElse(Constraints()) - - // Only modify child - val newElement = node.record.element match { - case e: Constrainable => - e.applyConstraints(constraints) - case e => ??? - } - Some(node.record.copy(element = newElement)) - } - - - // Bottom-up pass - tree.traverseMapPost { node => - node.record.element match { - case e: ConstrainedSpacerRect => - None - case e: SizedSpacerRect => - None - case e: ConstrainedLogicRect => - None - case e: SizedLogicRect => - None - case e: PlacedLogicRect => - None - case e: ConstrainedHierarchicalTop => - val newElement = e.applyConstraints(node.children(0).record.element.toConstraints) - Some(node.record.copy(element = newElement)) - case e: SizedHierarchicalTop => - throw new Exception("Cannot propagate constraints to a SizedHierarchicalTop") - case e: PlacedHierarchicalTop => - throw new Exception("Cannot propagate constraints to a PlacedHierarchicalTop") - case e: ConstrainedWeightedGrid => - // TODO make this less repetitive with the below - // Preserve ordering of children and convert to 2d Seq - val children: Seq[Seq[Constraints]] = e.elements.map(x => tree.getRecord(x).element.toConstraints).grouped(e.xDim).toSeq - val widthConstraint = children.map(_.map(_.width).reduce(_ + _)).reduce(_ and _) - val heightConstraint = children.transpose.map(_.map(_.height).reduce(_ + _)).reduce(_ and _) - val areaConstraint = children.flatten.map(_.area).reduce(_ + _) - val newElement = e.applyConstraints(Constraints( - widthConstraint, - heightConstraint, - areaConstraint, - Unconstrained() - )) - // TODO handle the weights - Some(node.record.copy(element = newElement)) - case e: ConstrainedElasticGrid => - // Preserve ordering of children and convert to 2d Seq - val children: Seq[Seq[Constraints]] = e.elements.map(x => tree.getRecord(x).element.toConstraints).grouped(e.xDim).toSeq - val widthConstraint = children.map(_.map(_.width).reduce(_ + _)).reduce(_ and _) - val heightConstraint = children.transpose.map(_.map(_.height).reduce(_ + _)).reduce(_ and _) - val areaConstraint = children.flatten.map(_.area).reduce(_ + _) - val newElement = e.applyConstraints(Constraints( - widthConstraint, - heightConstraint, - areaConstraint, - Unconstrained() - )) - Some(node.record.copy(element = newElement)) - case e: SizedGrid => - ??? // TODO probably should sanity check here - case e: MemMacroArray => - val area = node.children.map(_.record.element match { - case e2: HasFixedDimensions => e2.area - case e2 => throw new Exception("All macro dimensions must be resolved at this point") - }).sum - val newElement = e.applyConstraints(Constraints( - Unconstrained(), - Unconstrained(), - GreaterThanOrEqualTo(area), - Unconstrained() - )) - Some(node.record.copy(element = newElement)) - case e: SizedMacro => - None - case e: PlacedMacro => - None - case _ => ??? - } - } - - // Sibling propagation to Grids - tree.allNodes.values.foreach { node => - node.record.element match { - case e: SizedGrid => - // Do nothing - case e: Grid => - // This look-up preserves ordering of children - val children = e.elements.map(x => tree.getNode(tree.getRecord(x).element.name)) - val wConstraints = Seq.tabulate(e.xDim)(iX => Seq.tabulate(e.yDim) { iY => - children(e.toIdx(iX, iY)).record.element.toConstraints.width - } reduce (_ and _)) - val hConstraints = Seq.tabulate(e.yDim)(iY => Seq.tabulate(e.xDim) { iX => - children(e.toIdx(iX, iY)).record.element.toConstraints.height - } reduce (_ and _)) - val newElements = children.zipWithIndex.map { case (child, idx) => - val (iX, iY) = e.fromIdx(idx) - // We can always assume this is Constrainable- but really we should fix this in the type system - child.replace(child.record.copy( - element = child.record.element.asInstanceOf[Constrainable]. - applyConstraints(Constraints(width = wConstraints(iX), height = hConstraints(iY), Unconstrained(), Unconstrained())) - )) - child.record.element.name - } - // Needed to be able to use copy - e match { - case e: ConstrainedWeightedGrid => - node.replace(node.record.copy(element = e.copy(elements = newElements))) - case e: ConstrainedElasticGrid => - node.replace(node.record.copy(element = e.copy(elements = newElements))) - case e => - ??? - } - case e => - // Do nothing - } - } - - } // End of iteration loop - - tree.toState - } -} diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanCompiler.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanCompiler.scala deleted file mode 100644 index 8d13f628..00000000 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanCompiler.scala +++ /dev/null @@ -1,76 +0,0 @@ -// See LICENSE for license details -package barstools.floorplan.compiler - -import barstools.floorplan._ -import java.io.{File, FileWriter} - -case class FloorplanOptions( - outFile: String = "", - topMod: String = "", - outFmt: OutputFormat = OutputFormat.HammerIR, - inFiles: Seq[String] = Seq(), - sbAnnoFiles: Seq[String] = Seq(), - memInstMapFiles: Seq[String] = Seq(), - debugFile: Option[String] = None -) - -object FloorplanCompiler extends App { - - val opts = (new scopt.OptionParser[FloorplanOptions]("fpCompiler") { - - opt[String]('i', "input-file"). - required(). - valueName(""). - action((x, c) => c.copy(inFiles = c.inFiles :+ x)). - text("input file name") - - opt[String]('t', "top-module"). - required(). - valueName(""). - action((x, c) => c.copy(topMod = x)). - text("top module name") - - opt[String]('o', "output-file"). - required(). - valueName(""). - action((x, c) => c.copy(outFile = x)). - text("output file name") - - opt[String]('m', "mem-inst-file"). - required(). - valueName(""). - action((x, c) => c.copy(memInstMapFiles = c.memInstMapFiles :+ x)). - text("file containing the memory instance map") - - opt[String]('b', "oob-anno-file"). - required(). - valueName(""). - action((x, c) => c.copy(sbAnnoFiles = c.sbAnnoFiles :+ x)). - text("output file name") - - opt[Unit]('f', "output-fpir"). - action((x, c) => c.copy(outFmt = OutputFormat.FloorplanIR)). - text("emit floorplanIR") - - opt[String]('d', "debug-file"). - action((x, c) => c.copy(debugFile = Some(x))). - text("debug file path") - - }).parse(args, FloorplanOptions()).getOrElse { - throw new Exception("Error parsing options!") - } - - // TODO make Passes customizable - val fpStateIn = FloorplanState.fromFiles(opts.inFiles) - val debugWriter = opts.debugFile.map(x => new FileWriter(new File(x))) - debugWriter.foreach(_.write("Input state:\n\n")) - val fpStateOut = Pass.all(opts).foldLeft(fpStateIn) { (state, pass) => - debugWriter.foreach(_.write(FloorplanState.serialize(state))) - debugWriter.foreach(_.write("\n\nNext state:\n\n")) - pass.execute(state) - } - debugWriter.foreach(_.write(FloorplanState.serialize(fpStateOut))) - debugWriter.foreach(_.close()) - FloorplanState.toFile(opts.outFile, opts.outFmt, fpStateOut) - -} diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanTree.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanTree.scala deleted file mode 100644 index 76da7dc0..00000000 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/FloorplanTree.scala +++ /dev/null @@ -1,138 +0,0 @@ -// See LICENSE for license details -package barstools.floorplan.compiler - -import scala.collection.mutable.{ArrayBuffer, HashMap} -import barstools.floorplan._ - -// TODO this is required to give access to Node types outside FloorplanTree. Ideally we should move all that functionality into this class -// with some type-generic methods, but for now this works -sealed trait FloorplanTreeNode { - - def parent: Option[FloorplanTreeNode] - def children: Seq[FloorplanTreeNode] - def record: FloorplanRecord - def addChildRecord(cRecord: FloorplanRecord): FloorplanTreeNode - def removeChild(n: FloorplanTreeNode): Unit - def delete(): Unit - def replace(r: FloorplanRecord): Unit - def reparent(p: FloorplanTreeNode): Unit - -} - -class FloorplanTree(val state: FloorplanState, val topMod: String) { - - val allNodes = new HashMap[String, FloorplanTreeNode]() - - class Node(val parent: Option[FloorplanTreeNode], initialRecord: FloorplanRecord) extends FloorplanTreeNode { - val _children = new ArrayBuffer[FloorplanTreeNode]() - - // TODO this might be dangerous - private var _record = initialRecord - - def children = _children.toSeq - def record = _record - - def addChildRecord(cRecord: FloorplanRecord): FloorplanTreeNode = { - val n = new Node(Some(this), cRecord) - _children += n - allNodes += (cRecord.element.name -> n) - n - } - - def removeChild(n: FloorplanTreeNode) { - _children.remove(_children.indexOf(n)) - } - - def delete() { - assert(_children.isEmpty) // sanity check that we aren't orphaning nodes - parent.foreach(_.removeChild(this)) - allNodes.remove(record.element.name) - } - - def replace(r: FloorplanRecord) { _record = r } - - def reparent(p: FloorplanTreeNode) { - this.delete() - p.addChildRecord(record) - } - } - - - def getUniqueName(suggestion: String): String = { - var i = 0 - var tmp = suggestion + s"_${i}" - while (allNodes.keys.toSet.contains(tmp)) { - i = i + 1 - tmp = suggestion + s"_${i}" - } - tmp - } - - def getRecord(s: String): FloorplanRecord = getNode(s).record - def getNode(s: String): FloorplanTreeNode = allNodes(s) - - // These are only used by the constructor - private val allRecords: Map[String, FloorplanRecord] = state.records.map({ x => (x.element.name -> x) }).toMap - private def _getRecord(s: String): FloorplanRecord = allRecords(s) - - val topRecords = state.records.flatMap({ r => r.element match { - case e: Top => Seq(r) - case _ => Seq() - }}) - assert(topRecords.length == 1, "Must be exactly one Top record") - val topRecord = topRecords(0) - - private def dfs(parent: Option[FloorplanTreeNode], r: FloorplanRecord): FloorplanTreeNode = { - r.element match { - case e: Top => - assert(!parent.isDefined, "Cannot have multiple tops") - val n = new Node(None, r) - // There's probably a better way to handle these - e match { - case e: ConstrainedHierarchicalTop => - dfs(Some(n), _getRecord(e.topGroup)) - case e: SizedHierarchicalTop => - dfs(Some(n), _getRecord(e.topGroup)) - case e: PlacedHierarchicalTop => - e.elements.foreach(x => dfs(Some(n), _getRecord(x))) - } - allNodes += (e.name -> n) - n - case e: Primitive => - assert(parent.isDefined, "Must have parent") - parent.get.addChildRecord(r) - case e: Group => - assert(parent.isDefined, "Must have parent") - val n = parent.get.addChildRecord(r) - e.elements.foreach(x => dfs(Some(n), _getRecord(x))) - n - case _ => ??? - } - } - - val topNode = dfs(None, topRecord) - - // Traverse using DFS, passing the node to a function which expects an - // Option[FloorplanRecord] return - // None = do no modify - // Some(record) = modify node - def traverseMapPre(f: (FloorplanTreeNode => Option[FloorplanRecord])) { traverseMapPreHelper(topNode, f) } - def traverseMapPost(f: (FloorplanTreeNode => Option[FloorplanRecord])) { traverseMapPostHelper(topNode, f) } - - private def traverseMapPreHelper(n: FloorplanTreeNode, f: (FloorplanTreeNode => Option[FloorplanRecord])) { - f(n).foreach { r => n.replace(r) } - n.children.foreach { c => traverseMapPreHelper(c, f) } - } - - private def traverseMapPostHelper(n: FloorplanTreeNode, f: (FloorplanTreeNode => Option[FloorplanRecord])) { - n.children.foreach { c => traverseMapPostHelper(c, f) } - f(n).foreach { r => n.replace(r) } - } - - def toState: FloorplanState = { - val records = allNodes.values.map(_.record).toSeq - val level = records.map(_.element.level).max - FloorplanState(records, level) - } - -} diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/MemInstMap.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/MemInstMap.scala deleted file mode 100644 index 949c070b..00000000 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/MemInstMap.scala +++ /dev/null @@ -1,29 +0,0 @@ -// See LICENSE for license details -package barstools.floorplan.compiler - -import scala.collection.Map -import scala.collection.mutable.HashMap -import java.io.File -import scala.io.Source - -import firrtl.annotations.TargetToken.{Instance, OfModule} - -object MemInstMap { - - def fromFiles(files: Seq[String]): Map[OfModule, Seq[(Instance, OfModule)]] = { - val hashMap = new HashMap[OfModule, Seq[(Instance, OfModule)]]() - files.foreach { f => - Source.fromFile(new File(f)).getLines.foreach { line: String => - val listRaw = line.split(" ") - val module = OfModule(listRaw(0)) - hashMap += module -> listRaw.toSeq.drop(1).map { x => - val s = x.split(":") - (Instance(s(0)), OfModule(s(1))) - } - } - } - hashMap.toMap - } - -} - diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/OutOfBandAnnotation.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/OutOfBandAnnotation.scala deleted file mode 100644 index 6e8e6eb2..00000000 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/OutOfBandAnnotation.scala +++ /dev/null @@ -1,46 +0,0 @@ -// See LICENSE for license details -package barstools.floorplan.compiler - -import barstools.floorplan._ -import scala.collection.{Seq, Map} - -case class OutOfBandAnnotation( - ofModule: String, - width: Option[BigDecimal] = None, - height: Option[BigDecimal] = None, - area: Option[BigDecimal] = None -) { - def ++(that: OutOfBandAnnotation): OutOfBandAnnotation = { - assert(this.ofModule == that.ofModule) - assert(!this.width.isDefined || !that.width.isDefined) - assert(!this.height.isDefined || !that.height.isDefined) - assert(!this.area.isDefined || !that.area.isDefined) - OutOfBandAnnotation( - this.ofModule, - this.width.orElse(that.width), - this.height.orElse(that.width), - this.area.orElse(that.area) - ) - } - - def widthConstraint = width.map(x => EqualTo(x)).getOrElse(Unconstrained()) - def heightConstraint = height.map(x => EqualTo(x)).getOrElse(Unconstrained()) - def areaConstraint = area.map(x => EqualTo(x)).getOrElse(Unconstrained()) -} - -object OutOfBandAnnotationMap { - def fromFiles(files: Seq[String]): Map[String, OutOfBandAnnotation] = fromSeq(OutOfBandAnnotationSeq.fromFiles(files)) - def fromFile(file: String): Map[String, OutOfBandAnnotation] = fromSeq(OutOfBandAnnotationSeq.fromFile(file)) - def fromSeq(seq: Seq[OutOfBandAnnotation]): Map[String, OutOfBandAnnotation] = seq.groupBy(_.ofModule).mapValues(_.reduce(_ ++ _)) -} - -object OutOfBandAnnotationSeq { - def fromFiles(files: Seq[String]): Seq[OutOfBandAnnotation] = files.flatMap(x => fromFile(x)) - def fromFile(file: String): Seq[OutOfBandAnnotation] = { - val source = scala.io.Source.fromFile(file) - val annos = OutOfBandAnnotationSerialization.deserialize(source.getLines.mkString("\n")) - source.close() - annos - } -} - diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/OutOfBandAnnotationPass.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/OutOfBandAnnotationPass.scala deleted file mode 100644 index 7b741137..00000000 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/OutOfBandAnnotationPass.scala +++ /dev/null @@ -1,94 +0,0 @@ -// See LICENSE for license details -package barstools.floorplan.compiler - -import barstools.floorplan._ -import scala.collection.Map - -class OutOfBandAnnotationPass(val sbMap: Map[String, OutOfBandAnnotation]) extends Pass { - def execute(state: FloorplanState): FloorplanState = { - val newRecords = state.records.map { record => - val ofModule = record.ofModule.getOrElse("") - val newElement = record.element match { - case e: ConstrainedLogicRect => - sbMap.get(ofModule).map(sb => - e.copy( - width = e.width.and(sb.widthConstraint), - height = e.height.and(sb.heightConstraint), - area = e.area.and(sb.areaConstraint) - ) - ).getOrElse(e) - case e: SizedLogicRect => - sbMap.get(ofModule).map(sb => - e.copy( - width = sb.width.getOrElse(e.width), - height = sb.height.getOrElse(e.height) - ) - ).getOrElse(e) - case e: PlacedLogicRect => - sbMap.get(ofModule).map(sb => - e.copy( - width = sb.width.getOrElse(e.width), - height = sb.height.getOrElse(e.height) - ) - ).getOrElse(e) - case e: ConstrainedHierarchicalTop => - sbMap.get(ofModule).map(sb => - e.copy( - width = e.width.and(sb.widthConstraint), - height = e.height.and(sb.heightConstraint), - area = e.area.and(sb.areaConstraint) - ) - ).getOrElse(e) - case e: SizedHierarchicalTop => - sbMap.get(ofModule).map({sb => - e.copy( - width = sb.width.getOrElse(e.width), - height = sb.height.getOrElse(e.height) - ) - }).getOrElse(e) - case e: PlacedHierarchicalTop => - sbMap.get(ofModule).map({sb => - e.copy( - width = sb.width.getOrElse(e.width), - height = sb.height.getOrElse(e.height) - ) - }).getOrElse(e) - case e: AbstractMacro => - sbMap.get(ofModule).map({sb => - assert(sb.width.isDefined && sb.height.isDefined, "Macro out-of-band annotations must include width and height") - SizedMacro( - name = e.name, - parent = e.parent, - orientation = Orientation.r0, - width = sb.width.get, - height = sb.height.get - ) - }).getOrElse(e) - case e: SizedMacro => - sbMap.get(ofModule).map({sb => - assert(sb.width.isDefined && sb.height.isDefined, "Macro out-of-band annotations must include width and height") - e.copy( - width = sb.width.get, - height = sb.height.get - ) - }).getOrElse(e) - case e: PlacedMacro => - sbMap.get(ofModule).map({sb => - assert(sb.width.isDefined && sb.height.isDefined, "Macro out-of-band annotations must include width and height") - e.copy( - width = sb.width.get, - height = sb.height.get - ) - }).getOrElse(e) - case e => e - } - record.copy(element = newElement) - } - newRecords.map(_.element).collectFirst { - case e: AbstractMacro => - throw new Exception("Unannotated macros still exist after OutOfBandAnnotationPass") - } - state.copy(records = newRecords) - } -} - diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/OutOfBandAnnotationSerialization.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/OutOfBandAnnotationSerialization.scala deleted file mode 100644 index 145033b6..00000000 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/OutOfBandAnnotationSerialization.scala +++ /dev/null @@ -1,19 +0,0 @@ -// See LICENSE for license details -package barstools.floorplan.compiler - -import org.json4s._ -import org.json4s.native.Serialization.{read, write, writePretty} - -object OutOfBandAnnotationSerialization { - - val formats = DefaultFormats.skippingEmptyValues - - def serialize(seq: Seq[OutOfBandAnnotation]): String = writePretty(seq)(formats) - - def deserialize(str: String): Seq[OutOfBandAnnotation] = { - implicit val formats = OutOfBandAnnotationSerialization.formats - read[Seq[OutOfBandAnnotation]](str) - } - -} - diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/Pass.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/Pass.scala deleted file mode 100644 index 8198c541..00000000 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/Pass.scala +++ /dev/null @@ -1,26 +0,0 @@ -// See LICENSE for license details -package barstools.floorplan.compiler - -import barstools.floorplan._ - -object Pass { - - def all(opts: FloorplanOptions): Seq[Pass] = { - val instMap = MemInstMap.fromFiles(opts.memInstMapFiles) - val sbAnnos = OutOfBandAnnotationMap.fromFiles(opts.sbAnnoFiles) - Seq( - new TransformMemsPass(instMap), - new OutOfBandAnnotationPass(sbAnnos), - new ReplaceHierarchicalPass(opts.topMod), - new ConstraintPropagationPass(opts.topMod), - new ReplaceMemMacroArrayPass(opts.topMod), - new ResolveConstraintsPass(opts.topMod), - new CalculatePlacementsPass(opts.topMod) - ) - } -} - -abstract class Pass { - def execute(state: FloorplanState): FloorplanState -} - diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceHierarchicalPass.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceHierarchicalPass.scala deleted file mode 100644 index 6ae44821..00000000 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceHierarchicalPass.scala +++ /dev/null @@ -1,102 +0,0 @@ -// See LICENSE for license details -package barstools.floorplan.compiler - -import scala.collection.mutable.{HashMap, HashSet} -import barstools.floorplan._ - -class ReplaceHierarchicalPass(val topMod: String) extends Pass { - def execute(state: FloorplanState): FloorplanState = { - - // TODO this pass is for resolving a design to a flattened floorplan. To - // support hierarchical PNR this will need to change - - // Find HierarchicalTop records, then stitch them together - val topMaps = (state.records.flatMap(r => r.element match { - case e: ConstrainedHierarchicalTop => Some((r.fullPath -> r)) - case e: SizedHierarchicalTop => Some((r.fullPath -> r)) - case e: PlacedHierarchicalTop => Some((r.fullPath -> r)) - case _ => None - })).toMap - - val scope = topMaps(topMod).scope - val scopeNameSet = HashSet(state.records.filter(_.scope == scope).map(_.element.name):_*) - - val nonScopePaths = topMaps.filterKeys(_ != topMod).values.map(_.scope) - - def getUniqueName(suggestion: String): String = { - var i = 0 - var tmp = suggestion + s"_${i}" - while (scopeNameSet.contains(tmp)) { - i = i + 1 - tmp = suggestion + s"_${i}" - } - scopeNameSet += tmp - tmp - } - - val renameMap = new HashMap[(String, String), String]() - - def rename(oldScope: String, oldName: String): String = { - if (oldScope == scopeNameSet) { return oldName } - val tup = (oldScope, oldName) - if (renameMap.contains(tup)) { return renameMap(tup) } - val newName = getUniqueName(oldName) - renameMap += (tup -> newName) - newName - } - - def getRelPath(fullPath: Option[String]): Option[String] = fullPath.map { p => - assert(p.startsWith(scope), "Full path must be in scope scope") - p.substring(scope.length) - } - - val newRecords = state.records.flatMap({r => r.element match { - case e: HierarchicalBarrier => - assert(topMaps.contains(r.fullPath), s"All HierarchicalBarriers must have a corresponding HierarchicalTop: ${r.fullPath} is missing") - val tr = topMaps(r.fullPath) - (tr.element match { - case t: ConstrainedHierarchicalTop => - // We replace with two elements (one to replace each name); they'll get optimized away later - // Parent is first (replaces e), child is after (replaces t) - Seq(ConstrainedElasticGrid( - name = rename(r.scope, e.name), - parent = rename(r.scope, e.parent), - xDim = 1, - yDim = 1, - elements = Seq(rename(tr.scope, t.name)), - width = t.width, - height = t.height, - area = t.area, - aspectRatio = t.aspectRatio - ), ConstrainedElasticGrid( - name = rename(tr.scope, t.name), - parent = rename(r.scope, e.name), - xDim = 1, - yDim = 1, - elements = Seq(rename(tr.scope, t.topGroup)), - width = t.width, - height = t.height, - area = t.area, - aspectRatio = t.aspectRatio - )) - case t: SizedHierarchicalTop => ??? // TODO not supported yet - case t: PlacedHierarchicalTop => ??? // TODO not supported yet - case _ => ??? - }).map(newE => FloorplanRecord( - scope = scope, - inst = getRelPath(r.inst.map(_ => r.fullPath)), - ofModule = r.ofModule, - element = newE - )) - case e: ConstrainedHierarchicalTop if r.ofModule != Some(topMod) => Seq() - case e: PlacedHierarchicalTop if r.ofModule != Some(topMod) => Seq() - case e => Seq(r.copy( - scope = scope, - inst = getRelPath(r.inst.map(_ => r.fullPath)), - ofModule = r.ofModule, - element = r.element.mapNames(x => rename(r.scope, x)) - )) - }}) - state.copy(records = newRecords, level = 2) // TODO recalculate level - } -} diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceMemMacroArrayPass.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceMemMacroArrayPass.scala deleted file mode 100644 index b80fc329..00000000 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/ReplaceMemMacroArrayPass.scala +++ /dev/null @@ -1,110 +0,0 @@ -// See LICENSE for license details -package barstools.floorplan.compiler - -import barstools.floorplan._ - -import scala.math.{BigDecimal, sqrt} - -class ReplaceMemMacroArrayPass(topMod: String) extends Pass { - def execute(state: FloorplanState): FloorplanState = { - val tree = new FloorplanTree(state, topMod) - // TODO this needs some additional options, etc. - val utilization = 0.8 // amount of area to devote to SRAMs - - // For the proof-of-concept, we'll just replace them with an Array that meets whatever box constraints - val nodes = tree.allNodes.values.filter(_.record.element.isInstanceOf[MemMacroArray]) - - nodes.foreach { n => - val e = n.record.element.asInstanceOf[MemMacroArray] - // TODO this assumes that the MemMacroArray contains all the same type of SRAM. This is usually true, but we do nothing to enforce this. - - // Try to size based on the SRAM - val mem0 = tree.getRecord(e.elements(0)).element.asInstanceOf[SizedMacro] - val numMems = e.elements.length - - val defaultWidth = (mem0.width * numMems) / utilization - val defaultHeight = mem0.height - val defaultArea = defaultWidth * defaultHeight - - val (width, height) = e.toConstraints.aspectRatio match { - case c: Unconstrained => - (defaultWidth, defaultHeight) - case c: Impossible => throw new Exception("Illegal impossible constraint") - case c: Constrained => - val h = c.eq.map(q => BigDecimal(sqrt((defaultArea * q).doubleValue))).getOrElse(defaultHeight) - (defaultArea/h, h) - } - - val stackHeight = (height / mem0.height).setScale(0, BigDecimal.RoundingMode.DOWN).toInt - val columns = (numMems + stackHeight - 1) / Seq(stackHeight, 1).max - val paddingColumns = (columns + 1) / 2 - val topPadding = height - (stackHeight*mem0.height) - val xPadding = (width - (columns*mem0.width)) / paddingColumns - val xDim = columns + paddingColumns - val yDim = stackHeight + 1 - - def addSpacer(w: BigDecimal, h: BigDecimal): String = { - val newElement = SizedSpacerRect( - name = tree.getUniqueName("spacer"), - parent = e.name, - width = w, - height = h - ) - n.addChildRecord(FloorplanRecord(n.record.scope, None, None, newElement)) - newElement.name - } - - val elements = Seq.tabulate(xDim*yDim) { i => - val col = (i % xDim) - val row = (i / xDim) - val group = 2*(col / 3) + (row*columns) - val spacerHeight = if (row == yDim-1) topPadding else mem0.height - if (col % 3 == 0) { - if (group >= numMems) { - addSpacer(xPadding, spacerHeight) - } else { - e.elements(group) - } - } else if (col % 3 == 1) { - addSpacer(xPadding, spacerHeight) - } else { - if ((group + 1) >= numMems) { - addSpacer(xPadding, spacerHeight) - } else { - val eOut = e.elements(group + 1) - // Mirror it for proof-of-concept - val myNode = tree.getNode(eOut) - myNode.replace(myNode.record.copy(element = myNode.record.element.asInstanceOf[SizedMacro].copy( - orientation = Orientation.my - ))) - eOut - } - } - } - - val widths = Seq.tabulate(xDim) { i => - if (i % 3 == 0) { - mem0.width - } else if (i % 3 == 1) { - xPadding - } else { - mem0.width - } - } - val heights = Seq.fill(stackHeight)(mem0.height) ++ Seq(topPadding) - - val newElement = SizedGrid( - name = e.name, - parent = e.parent, - xDim = xDim, - yDim = yDim, - elements = elements, // TODO how to arrange these correctly? - widths = widths, - heights = heights - ) - n.replace(n.record.copy(element = newElement)) - } - - tree.toState - } -} diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/ResolveConstraintsPass.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/ResolveConstraintsPass.scala deleted file mode 100644 index d17b3341..00000000 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/ResolveConstraintsPass.scala +++ /dev/null @@ -1,90 +0,0 @@ -// See LICENSE for license details -package barstools.floorplan.compiler - -import barstools.floorplan._ - -class ResolveConstraintsPass(topMod: String) extends Pass { - def resolveConstraints(elt: ConstrainedRectLike): (BigDecimal, BigDecimal) = { - // TODO this is a temporary strategy - elt.toConstraints.resolveMinDimensions() - } - - def execute(state: FloorplanState): FloorplanState = { - val tree = new FloorplanTree(state, topMod) - - // TODO this isn't a correct implementation; it just resolves the constraints seen - // by any individual element rather than looking at the entirety of the floorplan - - // Bottom-up pass - tree.traverseMapPost { node => - node.record.element match { - case e: SizedRectLike => None // Already sized - case e: ConstrainedSpacerRect => - val (width, height) = resolveConstraints(e) - Some(node.record.copy(element = SizedSpacerRect( - e.name, - e.parent, - width, - height - ))) - case e: ConstrainedLogicRect => - val (width, height) = resolveConstraints(e) - Some(node.record.copy(element = SizedLogicRect( - e.name, - e.parent, - width, - height, - e.hardBoundary - ))) - case e: ConstrainedHierarchicalTop => - val (width, height) = resolveConstraints(e) - Some(node.record.copy(element = SizedHierarchicalTop( - e.name, - e.topGroup, - width, - height, - e.margins, - e.hardBoundary - ))) - case e: ConstrainedWeightedGrid => - // TODO make not repetitive - // Preserve ordering of children and convert to 2d Seq - // FIXME this is a hack that assumes all of the elements are equally constrained in a row (height) or column (width) - // This is true for the current implementation of the constraints propagation but not generally true - val childDims: Seq[(BigDecimal, BigDecimal)] = e.elements.map(x => tree.getRecord(x).element.toConstraints.resolveMinDimensions()) - val widths = childDims.take(e.xDim).map(_._1) - val heights = childDims.grouped(e.xDim).map(_(0)._2).toSeq - - Some(node.record.copy(element = SizedGrid( - e.name, - e.parent, - e.xDim, - e.yDim, - e.elements, - widths, - heights - ))) - case e: ConstrainedElasticGrid => - // Preserve ordering of children and convert to 2d Seq - // FIXME this is a hack that assumes all of the elements are equally constrained in a row (height) or column (width) - // This is true for the current implementation of the constraints propagation but not generally true - val childDims: Seq[(BigDecimal, BigDecimal)] = e.elements.map(x => tree.getRecord(x).element.toConstraints.resolveMinDimensions()) - val widths = childDims.take(e.xDim).map(_._1) - val heights = childDims.grouped(e.xDim).map(_(0)._2).toSeq - - Some(node.record.copy(element = SizedGrid( - e.name, - e.parent, - e.xDim, - e.yDim, - e.elements, - widths, - heights - ))) - case e => throw new Exception("Illegal element type") - } - } - - tree.toState - } -} diff --git a/floorplan/src/main/scala/barstools/floorplan/compiler/TransformMemsPass.scala b/floorplan/src/main/scala/barstools/floorplan/compiler/TransformMemsPass.scala deleted file mode 100644 index bb2429a0..00000000 --- a/floorplan/src/main/scala/barstools/floorplan/compiler/TransformMemsPass.scala +++ /dev/null @@ -1,72 +0,0 @@ -// See LICENSE for license details -package barstools.floorplan.compiler - -import firrtl.annotations.TargetToken.{Instance, OfModule} - -import barstools.floorplan._ - -import scala.collection.{Map, Set} -import scala.collection.mutable.{HashMap, HashSet} - -class TransformMemsPass(instMap: Map[OfModule, Seq[(Instance, OfModule)]]) extends Pass { - - val nameSet = new HashSet[String]() - val renameMap = new HashMap[String, Set[String]]() - - def getUniqueName(suggestion: String): String = { - // This is ugly and probably slow - var i = 0 - var tempName = suggestion + s"_${i}" - while (nameSet.contains(tempName)) { - tempName = suggestion + s"_${i}" - i += 1 - } - nameSet.add(tempName) - tempName - } - - def execute(state: FloorplanState): FloorplanState = { - nameSet ++= state.records.map(_.element.name).toSet - // Need to update in two passes - val newRecords = state.records.flatMap({ record => - record.element match { - case e: MemElement => - // TODO fail gracefully if key does not exist - instMap(OfModule(record.ofModule.get)).map { case (inst: Instance, ofMod: OfModule) => - nameSet.remove(e.name) - val element = AbstractMacro(getUniqueName(e.name + "_" + ofMod.value), e.parent) - renameMap.update(e.name, renameMap.getOrElse(e.name, Set()) ++ Set(element.name)) - FloorplanRecord( - scope = record.scope, - inst = record.inst.map(_ + "/" + inst.value), - ofModule = Some(ofMod.value), - element = element - ) - } - case _ => Seq(record) - } - }).map({ record => - record.element match { - case e: MemElementArray => - val element = MemMacroArray( - name = e.name, - parent = e.parent, - elements = e.elements.flatMap(x => renameMap(x)), - width = e.width, - height = e.height, - area = e.area, - aspectRatio = e.aspectRatio - ) - FloorplanRecord( - scope = record.scope, - inst = record.inst, // should always be None - ofModule = None, - element = element - ) - case _ => record - } - }) - state.copy(records = newRecords, level = 3) // TODO recalculate level? - } -} - diff --git a/floorplan/src/main/scala/barstools/floorplan/firrtl/Annotations.scala b/floorplan/src/main/scala/barstools/floorplan/firrtl/Annotations.scala deleted file mode 100644 index 8fd30fe0..00000000 --- a/floorplan/src/main/scala/barstools/floorplan/firrtl/Annotations.scala +++ /dev/null @@ -1,38 +0,0 @@ -// See LICENSE for license details -package barstools.floorplan.firrtl - - -import barstools.floorplan.{Element, Group} -import firrtl.annotations._ -import firrtl.stage.{RunFirrtlTransformAnnotation} - -trait FloorplanAnnotation extends Annotation { - val fpir: String -} - -case class InstanceFloorplanAnnotation(targets: Seq[Seq[Target]], fpir: String) extends MultiTargetAnnotation with FloorplanAnnotation { - def duplicate(t: Seq[Seq[Target]]) = this.copy(t, fpir) -} - -case class MemFloorplanAnnotation(targets: Seq[Seq[Target]], fpir: String) extends MultiTargetAnnotation with FloorplanAnnotation { - def duplicate(t: Seq[Seq[Target]]) = this.copy(t, fpir) -} - -case class NoReferenceFloorplanAnnotation(target: Target, fpir: String) extends SingleTargetAnnotation[Target] with FloorplanAnnotation { - def duplicate(t: Target) = this.copy(t, fpir) -} - -object InstanceFloorplanAnnotation { - def apply(targets: Seq[Seq[Target]], element: Element): InstanceFloorplanAnnotation = InstanceFloorplanAnnotation(targets, element.serialize) -} - -object MemFloorplanAnnotation { - def apply(targets: Seq[Seq[Target]], element: Element): MemFloorplanAnnotation = MemFloorplanAnnotation(targets, element.serialize) -} - -object NoReferenceFloorplanAnnotation { - def apply(target: Target, element: Element): NoReferenceFloorplanAnnotation = NoReferenceFloorplanAnnotation(target, element.serialize) -} - -case class FloorplanIRFileAnnotation(value: String) extends NoTargetAnnotation - diff --git a/floorplan/src/main/scala/barstools/floorplan/firrtl/Passes.scala b/floorplan/src/main/scala/barstools/floorplan/firrtl/Passes.scala deleted file mode 100644 index afe0db56..00000000 --- a/floorplan/src/main/scala/barstools/floorplan/firrtl/Passes.scala +++ /dev/null @@ -1,114 +0,0 @@ -// See LICENSE for license details -package barstools.floorplan.firrtl - -import barstools.floorplan.{FloorplanSerialization, FloorplanRecord, FloorplanState} -import firrtl.{CircuitState, Transform, DependencyAPIMigration, VerilogEmitter, AnnotationSeq} -import firrtl.options.{Dependency, RegisteredTransform, ShellOption} -import firrtl.analyses.{IRLookup} -import firrtl.annotations.{InstanceTarget, ReferenceTarget, ModuleTarget, Target, IsComponent} -import firrtl.annotations.TargetToken.{Instance, OfModule} - -// NOTE: If you rename/add this transform, don't forget to update META-INF -// See the @note in the RegisteredTransform documentation -class GenerateFloorplanIRPass extends Transform with RegisteredTransform with DependencyAPIMigration { - - override def prerequisites = Seq(Dependency[VerilogEmitter]) - override def optionalPrerequisites = Nil - override def dependents = Nil - override def invalidates(xform: Transform) = false - - val options = Seq( - new ShellOption[String]( - longOption = "floorplan-ir-file", - toAnnotationSeq = (a: String) => Seq(FloorplanIRFileAnnotation(a)), - helpText = s"Set the floorplan IR file name" - ) - ) - - def execute(state: CircuitState): CircuitState = { - - def getInstancePath(t: Option[InstanceTarget]): String = t map { it => - (Seq(it.module) ++ it.asPath.toList.map(_._1.value)).mkString("/") - } getOrElse state.circuit.main - - def getRelativePath(scope: Option[InstanceTarget], inst: Option[IsComponent]): String = { - val scopePath = scope.map(_.asPath).getOrElse(Seq()) - val instPath = inst.map(_.asPath).getOrElse(Seq()) - assert(instPath.take(scopePath.length) == scopePath, s"InstanceTarget ${instPath} must be inside ${scopePath}") - val pathStr = instPath.drop(scopePath.length).toList.map(_._1.value).mkString("/") - inst.map(_ match { - case x: InstanceTarget => pathStr - case x: ReferenceTarget => pathStr + "." + x.ref - case _ => ??? // Shouldn't exist - }) getOrElse "" - } - - def newRecord(path: String, ref: Option[String], ofModule: Option[String], anno: FloorplanAnnotation) = - FloorplanRecord(path, ref, ofModule, FloorplanSerialization.deserialize(anno.fpir)) - - val list = state.annotations.collect({ - case x: NoReferenceFloorplanAnnotation => - val (scopeTarget, ofModule) = x.target match { - case y: InstanceTarget => (Some(y), Some(y.ofModule)) - case y: ModuleTarget => - assert(y.module == state.circuit.main, "ModuleTarget is only supported for the top module") - (Option.empty[InstanceTarget], Some(y.module)) - case _ => ??? - } - newRecord(getInstancePath(scopeTarget), None, ofModule, x) - case x: InstanceFloorplanAnnotation if x.targets.flatten.length == 2 => - val scopeTarget = x.targets(0)(0) match { - case y: InstanceTarget => Some(y) - case y: ModuleTarget => - assert(y.module == state.circuit.main, "ModuleTarget is only supported for the top module") - Option.empty[InstanceTarget] - case _ => ??? - } - val (instTarget, ofModule) = x.targets(1)(0) match { - case y: InstanceTarget => (Some(y), Some(y.ofModule)) - case y: ModuleTarget => - assert(y.module == state.circuit.main, "ModuleTarget is only supported for the top module") - (Option.empty[InstanceTarget], Some(y.module)) - case _ => ??? - } - newRecord(getInstancePath(scopeTarget), Some(getRelativePath(scopeTarget, instTarget)), ofModule, x) - case x: MemFloorplanAnnotation if x.targets.flatten.length == 2 => - val scopeTarget = x.targets(0)(0) match { - case y: InstanceTarget => Some(y) - case y: ModuleTarget => - assert(y.module == state.circuit.main, "ModuleTarget is only supported for the top module") - Option.empty[InstanceTarget] - case _ => ??? - } - val refTarget = x.targets(1)(0).asInstanceOf[ReferenceTarget] - // Note: This assumes specific behavior from ReplSeqMem, namely that it replaces the Mem reference with - // a wrapper instance named ${ext} that instantiates an external bbox named ${ext}_ext - val mem = IRLookup(state.circuit).declaration(refTarget) match { - case m: firrtl.ir.DefInstance => m.module - case _ => throw new Exception("Something went wrong, Mems should become ExtModule instances") - } - val ext = mem+"_ext" - // TODO do we want to replace this in the output annotation file... ? - val newTarget = InstanceTarget( - circuit=refTarget.circuit, - module=refTarget.module, - instance=ext, - ofModule=ext, - path=refTarget.path :+ (Instance(refTarget.ref), OfModule(mem))) - newRecord(getInstancePath(scopeTarget), Some(getRelativePath(scopeTarget, Some(newTarget))), Some(ext), x) - }) - - val filename = state.annotations.collectFirst({ - case x: FloorplanIRFileAnnotation => x.value - }).getOrElse { - val opt = options.head.longOption - throw new Exception(s"Did not specify a filename for GenerateFloorplanIRPass. Please provide a FloorplanIRFileAnnotation or use the --${opt} option.") - } - val writer = new java.io.FileWriter(filename) - writer.write(FloorplanState.serialize(list)) - writer.close() - - state - } -} - diff --git a/floorplan/src/main/scala/barstools/floorplan/hammer/HammerIR.scala b/floorplan/src/main/scala/barstools/floorplan/hammer/HammerIR.scala deleted file mode 100644 index cfabd050..00000000 --- a/floorplan/src/main/scala/barstools/floorplan/hammer/HammerIR.scala +++ /dev/null @@ -1,122 +0,0 @@ -// See LICENSE for license details -package barstools.floorplan.hammer - -import barstools.floorplan._ - -import java.io.{Writer} -import scala.math.{BigInt, BigDecimal} - -// This implements a small subset of HammerIR - -object HammerIR { - - def fromFloorplanState(state: FloorplanState): HammerIR = { - assert(state.level == 0, "Can only convert level 0 FloorplanState") - val placementConstraints = state.records.map({ record => - record.element match { - case c: PlacedMacro => - Some(PlacementConstraint( - path = record.fullPath, - typ = PlacementType.hardmacro, - orientation = Some(c.orientation), - x = toMicrons(c.x), - y = toMicrons(c.y), - create_physical = Some(false), - width = toMicrons(c.width), - height = toMicrons(c.height), - master = record.ofModule, - margins = None, - top_layer = None, // TODO need this for macros - layers = None, // TODO need this for macros - obs_types = None // TODO need this for macros - )) - case c: PlacedLogicRect => - Some(PlacementConstraint( - path = record.fullPath, - typ = PlacementType.placement, - orientation = Some(Orientation.r0), // TODO represent this in FPIR - x = toMicrons(c.x), - y = toMicrons(c.y), - create_physical = None, - width = toMicrons(c.width), - height = toMicrons(c.height), - master = None, - margins = None, - top_layer = None, - layers = None, - obs_types = None - )) - case c: PlacedHierarchicalTop => - Some(PlacementConstraint( - path = record.fullPath, - typ = PlacementType.toplevel, - orientation = None, - x = toMicrons(c.x), - y = toMicrons(c.y), - create_physical = None, - width = toMicrons(c.width), - height = toMicrons(c.height), - master = None, - margins = Some(PlacementMargins(c.margins)), - top_layer = None, - layers = None, - obs_types = None - )) - case c => None - } - }).filter(_.isDefined).map(_.get) - HammerIR(placementConstraints = placementConstraints) - } - - def serialize(h: HammerIR): String = HammerSerialization.serialize(h) - def deserialize(s: String): HammerIR = HammerSerialization.deserialize(s) - def toMicrons(l: BigDecimal): Double = l.toDouble -} - -final case class HammerIR private[hammer] ( - placementConstraints: Seq[PlacementConstraint] -) - -final case class PlacementConstraint private[hammer] ( - path: String, - typ: PlacementType.Value, // type is a keyword, so we use typ and rename it in the serializer - orientation: Option[Orientation.Value], - // TODO these should ideally all be decimal types, not doubles - x: Double, - y: Double, - width: Double, - height: Double, - // End TODO - master: Option[String], - create_physical: Option[Boolean], - margins: Option[PlacementMargins], - top_layer: Option[String], - layers: Option[Seq[String]], - obs_types: Option[Seq[ObstructionType.Value]] -) - -object PlacementType extends Enumeration { - val dummy, placement, toplevel, hardmacro, hierarchical, obstruction = Value -} - -object ObstructionType extends Enumeration { - val place, route, power = Value -} - -final case class PlacementMargins private[hammer] ( - // TODO these should ideally all be decimal types, not doubles - left: Double, - right: Double, - top: Double, - bottom: Double -) - -object PlacementMargins { - def apply(m: barstools.floorplan.Margins): PlacementMargins = PlacementMargins( - left=m.left.toDouble, - right=m.right.toDouble, - top=m.top.toDouble, - bottom=m.bottom.toDouble - ) -} - diff --git a/floorplan/src/main/scala/barstools/floorplan/hammer/HammerSerialization.scala b/floorplan/src/main/scala/barstools/floorplan/hammer/HammerSerialization.scala deleted file mode 100644 index 39e12d5c..00000000 --- a/floorplan/src/main/scala/barstools/floorplan/hammer/HammerSerialization.scala +++ /dev/null @@ -1,42 +0,0 @@ -// See LICENSE for license details -package barstools.floorplan.hammer - -import barstools.floorplan.Orientation - -import org.json4s._ -import org.json4s.FieldSerializer.renameTo -import org.json4s.native.Serialization.{read, write, writePretty} -import org.json4s.JsonAST.JString -import org.json4s.ext.EnumNameSerializer - -object HammerSerialization { - - /* - // Custom serializer for PlacementType enums - class PlacementTypeSerializer extends CustomSerializer[PlacementType.Value](format => ( - { case JString(s) => PlacementType.values.withName(s) }, - { case v: PlacementType.Value => JString(v.toString) } - )) - // Custom serializer for Orientation enums - class OrientationSerializer extends CustomSerializer[Orientation.Value](format => ( - { case JString(s) => Orientation.values.withName(s) }, - { case v: Orientation.Value => JString(v.toString) } - )) - */ - - val formats = - DefaultFormats.skippingEmptyValues + - new EnumNameSerializer(PlacementType) + - new EnumNameSerializer(Orientation) + - new EnumNameSerializer(ObstructionType) + - FieldSerializer[PlacementConstraint](renameTo("typ", "type")) + - FieldSerializer[HammerIR](renameTo("placementConstraints", "vlsi.inputs.placement_constraints")) - - def serialize(state: HammerIR): String = writePretty(state)(formats) - - def deserialize(str: String): HammerIR = { - implicit val formats = HammerSerialization.formats - read[HammerIR](str) - } - -} diff --git a/floorplan/src/main/scala/barstools/floorplan/package.scala b/floorplan/src/main/scala/barstools/floorplan/package.scala deleted file mode 100644 index e9ce1c68..00000000 --- a/floorplan/src/main/scala/barstools/floorplan/package.scala +++ /dev/null @@ -1,18 +0,0 @@ -// See LICENSE for license details -package barstools -package object floorplan { - -import scala.math.{BigInt, BigDecimal} -import scala.collection.Seq - -def gcd(a: BigDecimal, b: BigDecimal): BigDecimal = { - val scaleFactor = 1 << Seq(a.scale, b.scale).max - val scaledGCD = (a*scaleFactor).toBigInt.gcd((b*scaleFactor).toBigInt) - BigDecimal(scaledGCD, scaleFactor) -} - -def lcm(a: BigDecimal, b: BigDecimal): BigDecimal = (a*b)/gcd(a,b) - -type PrimitiveConstraint = Constraint - -} diff --git a/tapeout/src/main/scala/barstools/tapeout/transforms/FloorplanReParentTransform.scala b/tapeout/src/main/scala/barstools/tapeout/transforms/FloorplanReParentTransform.scala deleted file mode 100644 index d2a4a240..00000000 --- a/tapeout/src/main/scala/barstools/tapeout/transforms/FloorplanReParentTransform.scala +++ /dev/null @@ -1,58 +0,0 @@ -// See LICENSE for license details -package barstools.tapeout.transforms -// This needs to be in the tapeout package, not floorplan, because of the build dependencies (floorplan cannot depend on tapeout) - -import barstools.floorplan.firrtl.{NoReferenceFloorplanAnnotation, InstanceFloorplanAnnotation, MemFloorplanAnnotation, FloorplanAnnotation} -import firrtl.annotations.{InstanceTarget, ReferenceTarget, ModuleTarget, Target, IsComponent} -import firrtl.{CircuitState, Transform, DependencyAPIMigration, AnnotationSeq} -import firrtl.stage.Forms -import firrtl.options.Dependency -import firrtl.stage.TransformManager.TransformDependency - -class FloorplanReParentTransform extends Transform with DependencyAPIMigration { - - override def prerequisites: Seq[TransformDependency] = Forms.HighForm - override def optionalPrerequisites: Seq[TransformDependency] = Seq.empty - override def optionalPrerequisiteOf: Seq[TransformDependency] = Seq(Dependency[ReParentCircuit]) - override def invalidates(a: Transform): Boolean = false - - def transformTarget(t: Target, top: String): Target = t match { - case it: InstanceTarget => - if (it.ofModule == top) it.ofModuleTarget else it - case ot => ot - } - - def transformTargets(t: Seq[Seq[Target]], top: String): Seq[Seq[Target]] = { - t.map(_.map(x => transformTarget(x, top))) - } - - def execute(state: CircuitState): CircuitState = { - val newTop = state.annotations.collectFirst { - case ReParentCircuitAnnotation(x) => x.module - } - - // Convert the annotated top to a ModuleTarget, otherwise leave them alone and let ReParentCircuit handle them - val newAnnos = newTop.map(top => AnnotationSeq(state.annotations.toSeq.map { _ match { - // We probably want to check the sizes of these first - case a: NoReferenceFloorplanAnnotation => - a.target match { - case t: InstanceTarget => a.copy(target = transformTarget(t, top)) - case t => a - } - case a: InstanceFloorplanAnnotation => - a.targets(0)(0) match { - case t: InstanceTarget => a.copy(targets = transformTargets(a.targets, top)) - case t => a - } - case a: MemFloorplanAnnotation => - a.targets(0)(0) match { - case t: InstanceTarget => a.copy(targets = transformTargets(a.targets, top)) - case t => a - } - case _: FloorplanAnnotation => ??? - case a => a - }})).getOrElse(state.annotations) - - state.copy(annotations = newAnnos) - } -} From b0318c3e3d8d6ece5513c46875fca908fe6f95b6 Mon Sep 17 00:00:00 2001 From: Harrison Liew Date: Wed, 13 Apr 2022 15:05:06 -0700 Subject: [PATCH 70/73] fixed targetinstance casting --- src/main/scala/barstools/floorplan/firrtl/Passes.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/barstools/floorplan/firrtl/Passes.scala b/src/main/scala/barstools/floorplan/firrtl/Passes.scala index afe0db56..bf32548d 100644 --- a/src/main/scala/barstools/floorplan/firrtl/Passes.scala +++ b/src/main/scala/barstools/floorplan/firrtl/Passes.scala @@ -80,7 +80,7 @@ class GenerateFloorplanIRPass extends Transform with RegisteredTransform with De Option.empty[InstanceTarget] case _ => ??? } - val refTarget = x.targets(1)(0).asInstanceOf[ReferenceTarget] + val refTarget = x.targets(1)(0).asInstanceOf[InstanceTarget].asReference // Note: This assumes specific behavior from ReplSeqMem, namely that it replaces the Mem reference with // a wrapper instance named ${ext} that instantiates an external bbox named ${ext}_ext val mem = IRLookup(state.circuit).declaration(refTarget) match { From a929923c1d07237f87a172841974dd6360c2775b Mon Sep 17 00:00:00 2001 From: Class Account Date: Wed, 20 Apr 2022 14:19:23 -0700 Subject: [PATCH 71/73] fixed the path --- src/main/scala/barstools/floorplan/firrtl/Passes.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/scala/barstools/floorplan/firrtl/Passes.scala b/src/main/scala/barstools/floorplan/firrtl/Passes.scala index bf32548d..224411ef 100644 --- a/src/main/scala/barstools/floorplan/firrtl/Passes.scala +++ b/src/main/scala/barstools/floorplan/firrtl/Passes.scala @@ -87,15 +87,15 @@ class GenerateFloorplanIRPass extends Transform with RegisteredTransform with De case m: firrtl.ir.DefInstance => m.module case _ => throw new Exception("Something went wrong, Mems should become ExtModule instances") } - val ext = mem+"_ext" + val ext = mem // TODO do we want to replace this in the output annotation file... ? val newTarget = InstanceTarget( circuit=refTarget.circuit, module=refTarget.module, - instance=ext, + instance="", ofModule=ext, path=refTarget.path :+ (Instance(refTarget.ref), OfModule(mem))) - newRecord(getInstancePath(scopeTarget), Some(getRelativePath(scopeTarget, Some(newTarget))), Some(ext), x) + newRecord(getInstancePath(scopeTarget), None, Some(ext), x) }) val filename = state.annotations.collectFirst({ From f827a53c8e6a2b7fb0fbce91d276664ab9c949a7 Mon Sep 17 00:00:00 2001 From: Ruofeng Wang Date: Tue, 27 Sep 2022 15:46:41 -0700 Subject: [PATCH 72/73] save current working fp complier [skip ci] --- .../barstools/floorplan/Constraints.scala | 15 ++++++++ .../barstools/floorplan/FloorplanState.scala | 3 +- .../compiler/FloorplanCompiler.scala | 1 + .../compiler/ReplaceHierarchicalPass.scala | 7 ++-- .../compiler/ReplaceMemMacroArrayPass.scala | 26 ++++++++++++-- .../barstools/floorplan/firrtl/Passes.scala | 34 ++++++++----------- 6 files changed, 61 insertions(+), 25 deletions(-) diff --git a/src/main/scala/barstools/floorplan/Constraints.scala b/src/main/scala/barstools/floorplan/Constraints.scala index 8f5c89ee..cb1da59e 100644 --- a/src/main/scala/barstools/floorplan/Constraints.scala +++ b/src/main/scala/barstools/floorplan/Constraints.scala @@ -25,6 +25,21 @@ sealed trait Constraint { } BigDecimal(0) } + def getConstraint: BigDecimal = this.minimize match { + case c: Impossible => throw new Exception("This is not constrined") + case c: Unconstrained => BigDecimal(0) + case c: Constrained => + c.eq.foreach {x => return x } + c.geq.foreach { x => + c.mof.foreach { m => + val n = (x/m).setScale(0, BigDecimal.RoundingMode.UP) + m*n + } + return x + } + c.leq.foreach {x => return x} + BigDecimal(0) + } def isConstrained: Boolean } diff --git a/src/main/scala/barstools/floorplan/FloorplanState.scala b/src/main/scala/barstools/floorplan/FloorplanState.scala index b2a5bac6..1b4c6bf1 100644 --- a/src/main/scala/barstools/floorplan/FloorplanState.scala +++ b/src/main/scala/barstools/floorplan/FloorplanState.scala @@ -21,7 +21,8 @@ object FloorplanState { def fromFile(file: String): FloorplanState = { val source = scala.io.Source.fromFile(file) - val fpState = deserialize(source.getLines.mkString("\n")) + val sourceString = source.getLines.mkString("\n") + val fpState = deserialize(sourceString) source.close() fpState } diff --git a/src/main/scala/barstools/floorplan/compiler/FloorplanCompiler.scala b/src/main/scala/barstools/floorplan/compiler/FloorplanCompiler.scala index 8d13f628..406411a7 100644 --- a/src/main/scala/barstools/floorplan/compiler/FloorplanCompiler.scala +++ b/src/main/scala/barstools/floorplan/compiler/FloorplanCompiler.scala @@ -62,6 +62,7 @@ object FloorplanCompiler extends App { // TODO make Passes customizable val fpStateIn = FloorplanState.fromFiles(opts.inFiles) + val debugWriter = opts.debugFile.map(x => new FileWriter(new File(x))) debugWriter.foreach(_.write("Input state:\n\n")) val fpStateOut = Pass.all(opts).foldLeft(fpStateIn) { (state, pass) => diff --git a/src/main/scala/barstools/floorplan/compiler/ReplaceHierarchicalPass.scala b/src/main/scala/barstools/floorplan/compiler/ReplaceHierarchicalPass.scala index 6ae44821..e252375b 100644 --- a/src/main/scala/barstools/floorplan/compiler/ReplaceHierarchicalPass.scala +++ b/src/main/scala/barstools/floorplan/compiler/ReplaceHierarchicalPass.scala @@ -19,6 +19,8 @@ class ReplaceHierarchicalPass(val topMod: String) extends Pass { })).toMap val scope = topMaps(topMod).scope + println("top maps keys: " + topMaps.keys) + println("top maps :" + topMaps) val scopeNameSet = HashSet(state.records.filter(_.scope == scope).map(_.element.name):_*) val nonScopePaths = topMaps.filterKeys(_ != topMod).values.map(_.scope) @@ -83,7 +85,7 @@ class ReplaceHierarchicalPass(val topMod: String) extends Pass { case t: PlacedHierarchicalTop => ??? // TODO not supported yet case _ => ??? }).map(newE => FloorplanRecord( - scope = scope, + scope = scope + "type", inst = getRelPath(r.inst.map(_ => r.fullPath)), ofModule = r.ofModule, element = newE @@ -92,7 +94,8 @@ class ReplaceHierarchicalPass(val topMod: String) extends Pass { case e: PlacedHierarchicalTop if r.ofModule != Some(topMod) => Seq() case e => Seq(r.copy( scope = scope, - inst = getRelPath(r.inst.map(_ => r.fullPath)), + // not sure why the original way to do it doesn't work here + inst = getRelPath(r.inst.map(_ => r.fullPath)), ofModule = r.ofModule, element = r.element.mapNames(x => rename(r.scope, x)) )) diff --git a/src/main/scala/barstools/floorplan/compiler/ReplaceMemMacroArrayPass.scala b/src/main/scala/barstools/floorplan/compiler/ReplaceMemMacroArrayPass.scala index b80fc329..d7bb37cd 100644 --- a/src/main/scala/barstools/floorplan/compiler/ReplaceMemMacroArrayPass.scala +++ b/src/main/scala/barstools/floorplan/compiler/ReplaceMemMacroArrayPass.scala @@ -5,6 +5,9 @@ import barstools.floorplan._ import scala.math.{BigDecimal, sqrt} +case class AreaConstraintViolationException(msg: String) extends Exception(msg) +case class AspectRatioIncompatibleException(msg: String) extends Exception(msg) + class ReplaceMemMacroArrayPass(topMod: String) extends Pass { def execute(state: FloorplanState): FloorplanState = { val tree = new FloorplanTree(state, topMod) @@ -17,6 +20,19 @@ class ReplaceMemMacroArrayPass(topMod: String) extends Pass { nodes.foreach { n => val e = n.record.element.asInstanceOf[MemMacroArray] // TODO this assumes that the MemMacroArray contains all the same type of SRAM. This is usually true, but we do nothing to enforce this. + // Check if all the SRAMs are the same type + val sram_width = tree.getRecord(e.elements(0)).element.asInstanceOf[SizedMacro].width + val sram_height = tree.getRecord(e.elements(0)).element.asInstanceOf[SizedMacro].height + for (i <- e.elements) { + val next_sram = tree.getRecord(i).element.asInstanceOf[SizedMacro] + assert(next_sram.width == sram_width && next_sram.height == sram_height, "The SRAMs in " + e.name + " module have multiple types. This floorplan complier only supports same type of SRAMs") + } + + + val constrainedArea = e.height.getConstraint * e.width.getConstraint + if (constrainedArea < e.area.getConstraint) { + throw AreaConstraintViolationException("Area Constraint Violation is violated. The constraintArea(" + constrainedArea + ") is smaller than required area for all SRAMs(" + e.area.getConstraint + "). Please give more budget to this module.") + } // Try to size based on the SRAM val mem0 = tree.getRecord(e.elements(0)).element.asInstanceOf[SizedMacro] @@ -25,7 +41,7 @@ class ReplaceMemMacroArrayPass(topMod: String) extends Pass { val defaultWidth = (mem0.width * numMems) / utilization val defaultHeight = mem0.height val defaultArea = defaultWidth * defaultHeight - + val (width, height) = e.toConstraints.aspectRatio match { case c: Unconstrained => (defaultWidth, defaultHeight) @@ -42,6 +58,10 @@ class ReplaceMemMacroArrayPass(topMod: String) extends Pass { val xPadding = (width - (columns*mem0.width)) / paddingColumns val xDim = columns + paddingColumns val yDim = stackHeight + 1 + + if (topPadding <= 0 || xPadding <= 0) { + throw AspectRatioIncompatibleException("The aspect ratio can not be satisfied. Please try another aspect ratio for module: " + e.name) + } def addSpacer(w: BigDecimal, h: BigDecimal): String = { val newElement = SizedSpacerRect( @@ -93,18 +113,18 @@ class ReplaceMemMacroArrayPass(topMod: String) extends Pass { } val heights = Seq.fill(stackHeight)(mem0.height) ++ Seq(topPadding) + val newElement = SizedGrid( name = e.name, parent = e.parent, xDim = xDim, yDim = yDim, - elements = elements, // TODO how to arrange these correctly? + elements = elements, widths = widths, heights = heights ) n.replace(n.record.copy(element = newElement)) } - tree.toState } } diff --git a/src/main/scala/barstools/floorplan/firrtl/Passes.scala b/src/main/scala/barstools/floorplan/firrtl/Passes.scala index 224411ef..4b655d35 100644 --- a/src/main/scala/barstools/floorplan/firrtl/Passes.scala +++ b/src/main/scala/barstools/floorplan/firrtl/Passes.scala @@ -1,13 +1,16 @@ // See LICENSE for license details package barstools.floorplan.firrtl -import barstools.floorplan.{FloorplanSerialization, FloorplanRecord, FloorplanState} +import chisel3.UInt +import barstools.floorplan.{FloorplanSerialization, FloorplanRecord, FloorplanState, HierarchicalBarrier} import firrtl.{CircuitState, Transform, DependencyAPIMigration, VerilogEmitter, AnnotationSeq} import firrtl.options.{Dependency, RegisteredTransform, ShellOption} import firrtl.analyses.{IRLookup} -import firrtl.annotations.{InstanceTarget, ReferenceTarget, ModuleTarget, Target, IsComponent} +import firrtl.annotations.{InstanceTarget, ReferenceTarget, ModuleTarget, Target, GenericTarget, IsComponent} import firrtl.annotations.TargetToken.{Instance, OfModule} +import java.lang.Exception + // NOTE: If you rename/add this transform, don't forget to update META-INF // See the @note in the RegisteredTransform documentation class GenerateFloorplanIRPass extends Transform with RegisteredTransform with DependencyAPIMigration { @@ -46,7 +49,7 @@ class GenerateFloorplanIRPass extends Transform with RegisteredTransform with De def newRecord(path: String, ref: Option[String], ofModule: Option[String], anno: FloorplanAnnotation) = FloorplanRecord(path, ref, ofModule, FloorplanSerialization.deserialize(anno.fpir)) - val list = state.annotations.collect({ + val list: Seq[FloorplanRecord] = state.annotations.collect({ case x: NoReferenceFloorplanAnnotation => val (scopeTarget, ofModule) = x.target match { case y: InstanceTarget => (Some(y), Some(y.ofModule)) @@ -80,24 +83,17 @@ class GenerateFloorplanIRPass extends Transform with RegisteredTransform with De Option.empty[InstanceTarget] case _ => ??? } - val refTarget = x.targets(1)(0).asInstanceOf[InstanceTarget].asReference - // Note: This assumes specific behavior from ReplSeqMem, namely that it replaces the Mem reference with - // a wrapper instance named ${ext} that instantiates an external bbox named ${ext}_ext - val mem = IRLookup(state.circuit).declaration(refTarget) match { - case m: firrtl.ir.DefInstance => m.module - case _ => throw new Exception("Something went wrong, Mems should become ExtModule instances") + val (instTarget, ofModule) = x.targets(1)(0) match { + case y: InstanceTarget => (Some(y), Some(y.ofModule)) + case y: ModuleTarget => + assert(y.module == state.circuit.main, "ModuleTarget is only supported for the top module") + (Option.empty[InstanceTarget], Some(y.module)) + case _ => ??? } - val ext = mem - // TODO do we want to replace this in the output annotation file... ? - val newTarget = InstanceTarget( - circuit=refTarget.circuit, - module=refTarget.module, - instance="", - ofModule=ext, - path=refTarget.path :+ (Instance(refTarget.ref), OfModule(mem))) - newRecord(getInstancePath(scopeTarget), None, Some(ext), x) - }) + newRecord(getInstancePath(scopeTarget), Some(getRelativePath(scopeTarget, instTarget)), ofModule, x) + + }) val filename = state.annotations.collectFirst({ case x: FloorplanIRFileAnnotation => x.value }).getOrElse { From 75018898dc19d99955e0c341b4697b70a8cd8ecc Mon Sep 17 00:00:00 2001 From: Ruofeng Wang Date: Thu, 17 Nov 2022 20:56:10 -0800 Subject: [PATCH 73/73] floorplan tools fixed bugs and add functions to be more flexible --- .../barstools/floorplan/.metals/metals.mv.db | Bin 0 -> 69632 bytes .../barstools/floorplan/.vscode/settings.json | 5 + .../compiler/ReplaceMemMacroArrayPass.scala | 15 +- .../compiler/ResolveConstraintsPass.scala | 252 +++++++++++++++++- 4 files changed, 259 insertions(+), 13 deletions(-) create mode 100644 src/main/scala/barstools/floorplan/.metals/metals.mv.db create mode 100644 src/main/scala/barstools/floorplan/.vscode/settings.json diff --git a/src/main/scala/barstools/floorplan/.metals/metals.mv.db b/src/main/scala/barstools/floorplan/.metals/metals.mv.db new file mode 100644 index 0000000000000000000000000000000000000000..963e0e8933c101a0487e83fdef2ee57a019bcb39 GIT binary patch literal 69632 zcmeHwU5p!9b{@%Y{cLNrf1X{hcRe##9L-EqYEf0if6i!j+0AOHcl$@vr2aB9%M^=6 zH7z#TL$W3H><+R)5<7P6B(PySyRkQm*a!mnX&v|}#$tgi^0G)^7|9Ou5F3X56u{5P zLjdPH_ui_yRV=D0jkM#y)e=~L_ug~vIp>~x?m6dHU8@=^?f%fdSJgFf(&)YK;6l?@ z?7hRmy=rO2?mO0?s;}51$C@~us$S0L3i*OoY-=ms;b`BQpppB|XxtkPP`ulBCib2) zs@kP;!Dx3YONsa|ra(-Am;x~cVhY3*h$#?LAf`Y}ftUg@1!4-s6!RvfpymzSMw{Q;c!y5ks6>G1q1m4lit2lH9EEz{LigShIZBJw%ODFqb!1S;#U&M zWD;jTOeAu*`7!yUKYih)6TmpzO(agPpZurEBWGa#z&r4LZ)ltnQ|4gzB}+ z70q^Xyk1AE6s@i<>q&UIdEi;LrB|!~p7|%NSD2?>J7-l2Kpa;01rbcyU= zIl5kuo@)OjJuA`mK+m$CM+yx15CzYe!G|EcY5o~A_(1&Y=u!L=cp5Y4iF&$LDa^(b zsXjc#CnPUHJg4bm%%CUHrJ_aAMc@g|5(%$UEL-_*6#ryBBf8#fctWmoGw2!UUs=nw zb6x!;AAc=X+;TI?YH7OO4UqLkUj1;;8Ey{mX2bk9f{(-9F+Rs@4-cF{b7T$1mOVjc z)%aCKC1?>EKw|z1FV-Knx2%KdQr&#Mq~$B+jA+c+KiHg6vQV*#mR8cwyY1~dUEZE! zwH?hUNxfrr45OqA+yJz0O{{6CZd*mA3v)1|YZNT2)Gmo?g84JYyxq~AN`MI}w7EoE z48cSqRXFiVD)~}^tKu#fg>36A;hq&p=z8F)7`UpaS4J*yRR~;_)vKHqxH1A)h36FL zMfIXAE+pyC@M41b-Z>b@B`O=1C{a}m4hh|zaRhXCJ_uap16Q&~vZU;hxH2S_)O7A9}WGQ9nDZ@x4lKknPdIleP;`{n6^S^*SR&O zaU-q5ZL~Zrgn{#LQq2WTwLF<>TDMp(3RA7HayU2~BZJdw%d^_V*Y)>3>w=89ypdlS z+VU`^s0Dq#21{22el6o@GhQ{Y)qAiVx|m2l2aqpw&UtyAe1pZ=1Xu_XaN zFDD$K)M?@Gt^W-jdR;gG3PNeq-T+N2I#xw`0NPsH@>~FUuBG)(vGBCG)2``y+BLXWhNHXLJtMn+e>@qEob2RbR!+T6 z=cd&^becy8&iQrN`oq!DE1|-jc6N98fNX2E)Zz&tmEqD8w3J;vnmD!5$U1Usy*%CK z_F=!zVY_9X0g?-g9LZ?80N< zu#m#vob4{6=)#<3mr(W-bC%`OvzSUIk}M>?T*xhvd;&OCSalb`zZXFmHg&wuXoKl_EB`{I{={)HD`V&~E?pHG})uikE$O}4gc)|zH( z^~T0#of&#Q&C+N8aZ=o6HyY+H+gNAYJ56TZ-e@!%EZwN3S);buWUoPW>V;yZ(9RVd z%_??ZW7lepYwR@``S9piWydO(3KcxfIeMvEE@B&*ZFTYv)Nwvn)GWP_%NHt6u3XIL z9F)oxi(Rc;sl3K&^{tKVml-?zmkSb;wc6S>v(B2e)lHM7uiseR+*nIz)7}2j1M8?Y z#ul`FtF_l7b$gUvUZUwJ-5ZQ2R=@9bT5zn~OS6sbrg;?|qTX!Z*xXzZ#pEoUW^31K zyS23@O5CjN-r3l`x?IpQvQWnv+oRqA`DUUD8k&{~w@X4BU3RLdXgjJf&A(H{(Odi8 z_;BCrl0?$)aQkRRV^eMUb*N{}jV-g$tZiLqb#uLT16*FavAb(-H(UOb5c!;kj(tdU zX;Fm0Ln%y+@u6)y<8hj;?(A%uwQcYDQsz86ch-`8zP`J$1>&=>nRgU6%eyVyZ57jO zXIptTLj=Oy&%DB3Ir}?F2rl?LpBO<5>uhajyV2aOL3WOV**buXDfll>p#TZ2o41vA zg8h0GbQnlFgiA*DvNdk?IuGZhhsOXC{RUg#*ljd3uVIXR+a2vQ##r6FZUXP^wL7iG z&W+tQ(;Z(;^LDdU-&wn{MFMhTcOydPJT^-a<$9*otnFSkkFBzlSq4S^{@jvL2~i>f z@$@v#nG6KuU(67U=_Y0ig)b>BU=k372&j*gg-ZGY81ubj7;|f9_iK%7CI~r=rc{B` zfTy)wm_>fG$FOuMa}oUcg}M2&9^#J=C<6Z|POX4b-=4v#M>RGV&w$-W;@Nuo5_tBD z$MCGN3T|y&g_3ng(N?_>F>d5=fi7;|Xpmms#)O1vD>!xCx@K;-cFnb&-MU#qT^?8f@WTCKUUv+d%wfd=J_DUspOyh;i<^|^Wb`o^xwQI=E5 zQsy#{X-P4ep=8#s?KIFAsU9#zxM(PM9wE9*nZ-{-7XRMdvREvKgfRkvv;sS`_!$V_ zFV7IZ>B@779Hz2>$f@uisULv-;%7nBe|rpv19-ufyJ_yabd46fuz)qpO%oPTy(KG8 zvq0s2q$Do>3>b4`ZpKt{k7A75hcuy|S$rNm`6u)6#6^GRjIJtgU=UynhxJH)0O`fg zfgc|p!w){IgPLEhd3;cY8M|t3o4c^a>umLoXMoijYy-1zbEA2OHO#keU?Ns6;KrRs z)7%1o#FN%qetq543Z+&7_H-+6tmj&_Vlme;aavn9b-h&9(qT3qBMcZ_pJy+heJGje z!cq*GC&PleCT(&ky4@zbwb8uBbhc`4?rdLew3<7x6kzZ#{wzf3T?zfh_6E8-#SVpm zoXRaTBu4?-r-|&UA+-1fh|>Gf)}dN+u5k^}0?TEBlbHwC#h*hLH|HKDkM5ip9A89_ zzWKCz1b~k@Cd~BWmoOxL=a?bU+_}DK-ZVE`jXPVbzS@^p)A}YXy4P#XYZ3EjxEPH% zPuSd96mtFZAp6^r?3~YvP)~_eST#-Ip5|Zza`ghZ@SkU>sBrzcgbPTl@9dfzSGQ@A zw9G-yA!4YuYp$DE%dMGM#d|AgRk)?NVtG}E-fx<0t=3qp)lCo+tM9e1Exrh9wdWSn z`N*{bWQd12N~UKPUjos7{izZSevN?q5~Cs@PYqRBiLX4N+haRa>vsTlIXsrq@dcA6-?)ECaxf-VlZmsLH{M|D+y%!P+y%$3 z2^SpWxgJcuM2fxn<_4e0>xuP7?aW_%=gYtQPZ)Thwzt>;-ozG{uw_-Ma9>~jom-OROGxYv^Rc-0^M!FPY0lvV$P zs$Q&}H}lumdxN{q=wQ?vOnBYfH}kEQdXppn=fC<_pOzeD9bn4mE{CWSAIFn!-`#VL_2_;-{G)Yf+|| zM5l9fe^X-UrX&|-O0t`0(ni*xp9PasLzR@O%+p{>!8DktjOE#Qc{$8VmWfslWSN{1 zAyCa|>qU94mr4ako)^m<%{zB;8fBV%E0MVPd9V-}CJ(-j^pYPYPX5{_6A6T{PW~kx zM)8UT5&`x zj%Xd1))hyz+6h)j-ocSI>d0qpYlK8| zDm5P2m-nsS;PTkE`qpLtLAO5~jt=_P;Icj3Kj`(H(dAudJnY|h*08&OGD4ln__{Sl zY>R51{cv*mGHW;vgZ*f3H%x|at+0O2b_QdIb&%uV>yMWXEc+g|BQOt4)&rAu!7MHH z_78@mNo1jx`;Y_8;$tp**>9}&?$#ZN{+L6n}>Q7NZ^G*qkvsVnoe}bN7g6^8-kBGW4KK##xEUR&P*;|9U{*tr;(8 zP#FCbxQYyRXBoQ%CQcOrP06|jT?4z|4G$;00fbFPVA&Nma31)jnz$$0T491#8G5yQ41*q}CjfyfgD zG{Lrq1Bdus>mxAS0eh){Y}l{naoFiJFf5!ANt2HOK_LDtKj?KPdn;_u>D}F%kUT{R zCP{<)*BI_Q%hVmW1NQLX2(rt0gZdm_AEd~$b}KtsdV7KJI{EhwENmK@1n&yG6u3;{ zsNBkAe8gOGDgDOFMtb&7wc8%Vr{3!zah&WPlapb9nGm z6NeDod8h-$aCJ01JP3Atrc?xrIndmryV$uCzWromyTu2G)44k8b%Hc=)$YD^&%vlB zZFJyFdK0_`Lg5NZUB|GZL=0@)>o_zmkWv97`W=WqbW?{^7jygb5p>9V(0BbKUpG1(C_-q~(eMFVb_YIY zQ+wDy+#g7l;Eyb!mnGoI=+NO)FW0t*P|bs|YIc!Ob@9Pyh^Q&o+^l^#;iS6j3?Pk! z88xc(mh=)Q4d$aU6rl6o+q34sccE|`SA~wj^@HVyq$9Z&zUxdd`a$sJhZ(obTyvk= zcS43s-58)etg;g@eA&r91d16Bwv}z^;d+9$$5%Au{3kTxTnd&%ah_omrCJ5r;h}Ma zu7vNoaumET$Pv6JyQ*;U80Bojf|~T~$RQOEmUytx*pD1#G*}cdgKmYf-Rt%g8;EPH z>zkyEN$o;Iar297%i#bDXAkBX+j91|EPFKM?*&j=ypLSB-nfPOSHhZ!)*NK&us1F# zYA2&uZDI(!>Sr5f5|z)mwvrrBENtw#wve>n-j`;d934!-&Mz4N^1iF!#>-mfF~EuD zxbgH*0_8*rPIEp`IuoTy&AMdjTK%!A!Ts_dM4-;=yIu4x#b}VCFgp&K3P(NXd58T0 zwwe-0s|-2K{0xpDAW6okJ6-J$JBNLziULkR9n%D`=UJ67p}~2f?Xs9){0B-$`JCZD z;Z?ZO4Alr#LA4eyh6+N*LToyRwzJ&2*b2`rR9K*a(o*}*DBkO0npe4xUC^&JriD_K`Ri+tR-3LduNIJi0^{`Yl!0hr z_IL>(?#Y4Z2fN7Lmx4Oo1&Nv*T;94gL>hG<$wv{lNZKJ3QlMHkmf(bnB2||Hcur$; zoW(O49a-ap)Nmn0Xkc($=N%WE z1zC4dlf^K>+lThPVbd%at=ut}(8Kv>N5U&KM+PPkmqhP#KLd4+ci)|M`2ZjBTSSKU zv%G&e6GV7j?Js0l>AhcC5g~z%`|AxR7YPnqf+m;(B%gC;Q=QyyZj1gg>fU3jA#Y9e z1V)LAh^vXbu^nOye!)5Lb(dH{j=2-FU|6Jh2637tU-OgGRbztbQj4jY;gZ_B z2p-L;#T3N|r3Jm|19X*`rYbR}DlO_XcgwoHkJJ{&VZvQ>(Zt z2UmmQ{`87c+;?@)bRABLkkf|hNc$#0^q_wC2MCu@(l^nT!Uo=Va~Cs~s}lH2Xj~mE zT9E|i^Y&!Oo!o#-&&Y8vCnISxkerBMg=#loY9hBPp9`a&I%4tLvgnm$ivPTcd5eZ8 z{IRX!sP!P&=48?j-9YhBZFu87?m~kJduR5%VGbs6emD92%GF}<$D0{w;sbbg1qP97 zltmh2>xjj`9BVeQfll*4wyJ-b)-vtIYi*SAbAHN+6vCNE_oV1L~S1A@klMa>KNAM z@eXmWhkPDql8ty;v%L>`Vy*=J%=8E?q zi>|Mc(^2}xyugjT9=J5dUNOACi-#KAU%yYD*WJIxapJ&SBMa+tNIra&C?v-(cXM)d zJsH45X*a5*2j;@;CNZHRywX$&-U^=uF^*_S9+W6mP9GgQ}=aVkZBgHE;sdQ2Q1$`tp+ui`K6a@DduoGb%Z@IKT| zV5WsYldSCx`yDc|C9TKM^pm|IT-pM05d;)~xb$Lv;Ndr^53Zp)bP~d3&U-U?PxL_h z$9b7ZDW6m6g%~%&qX>I&pVKt;2(7^^gl0Sd+kuKAnAC{MJR0yzOP4PwML;S+dmfVz ze*Ot&glQ7Zo|%FZk$QD8U=GR_Fs8jxOJ+YNe-SC6r4Ws2_Q2uM5JV#AHEUpDmhAEH z;%ClbM?{!C^dh_rRGtF5j~IOl0jYWaZ;D7*EgMgQyQ-DBzJ9H-7 zio%_bum=wi}4q%i|bqXCZ;1B~vV*XS`8xg0;3gdsU0P@-l1b&rW@uIEV^ zWwTj9DMBksLC+wafJTkG-2KQ+s#Ij$0m$*b!t~7auyCOYfss0cv-~kT;yMx{d9R}M z!P14x>Vk^2Iw43Jd^qae9bjnyUlZj@ATy*lRQOAC543knJjN?C@ffUjy}|4(zDNCE$BU4Ci3DBnyy1?|T+GOMcalVGMY(>FMrDBk8f_wNy zv#chFw`(DQ?>-=oO4^Vhq9@{xv{S_`adQeY*Ege%G%!u9yAHu70y;aC(7 zOi7f1aLGn3_^Yj>HlLSln1Kunc(qFcDe^BvFxZ$WS=x?~M5K9mbUzTjSpWUqycBDvkX^^9}d84$B}UZg^P%zldbRE+Rh-KU}G=|fe?ulBp%C>>=1hu?j>kL zfP@W5d2lW|@Lp3c!NX8%2UxTL<{Hn=;ElTNUlh$4p$Zsz*F_y*AnARHsgN$HoekA~ z=+_m=VGMO8L{X0u8+#}(BkN5?E!+^?LO{Sr_K~H@S%lIGIY2W#q^dhk`|z#;M%6?7 z8RZkYSn=hi*PAM-9ECDaIurxE$h;EM)`i==pdo^XC@$&At8+q#+d=?mH=*=PR8Vf9 zRp27&B%b<23md2|PULhGW$8>bK{OQiI5lTuBZnFp#FnU_4!+s-NNm^xPFVO#h#Xvy zyv_@XD#As0c}^s*5j{&ojttR^KTKv7cTv7Y+AV-Hze9kQLkM|Fj~-^|A;|2uqw-YV zyC&40i?SkTP~!pTl*q7ky>&`K?kDqy-y2M0-~6U8VsAigPcO?NooG-o2uJfS1wgyj z9aec0-Y>iu=pNh&6o=hELhL64-%ZIZo0bbuDE*^sV4zRM(U7f8U+6V?l_@u?=H7nPQK>=j0ZgKG*ZKoMMC;PQ+DcPaG` zMT`ridZ1}?8FY3T^)ASvG2N(GT}mci;ie~tbA|EIz;&*o0C<2}5mkkY!fqwNudrOC zB}Sr5ycT%A2|K`@UZ;&*DhoZ?;BJ6rSKT0$oZk@mx}N_jHwM<)U<4Q$-EQ=vU7!`D z5MiNm*lU=&p>!1!(=@0jM1Lmg1XI$E&tV@Q0lA>>!1yOqF3MG-pc#-5q9^@P1N%o} zIp`Ugbh+%H*=Zh79Z?=58T%iQ-QivGeDF{@b@1VFb}BqTNXCvtlmKIegKQ{ai(X+n z{#ABg%_63v;x8Cvaql8bguYEd35l2TTzx_W;}b@tVu{o#Brc>(W{*XG8d9JJMBJ+% zDd6s?jH*379A-gNYKMC)rW?1*@H_TZdn_Ytbx{bK?jko0ePyMJ5z!CEu^EgnOh@y$ zLzR}NbmGSbh=)onAu6iJ4T>vVGY9K&Ju1{`@PRT_xTnDa9>c{+@Szvq!yuG&QYfkR zov*gsVIwc128~ajz)FGE6aoMZcmKk*>S!-aRK67z<%r8_;{2i%`Wd|MdfA6hI|VzSExaDd#L$n7HMdTt(zgkwKVF|LP-L?Ttx7;4 zBakpJl&R`*cQik$%i)3U5A#RW3Iwlav3FWcNF@w1)yN#&;glfc2kt9skDo>uP^2X&68~n@O2SE&f`8Z0>NfSvvE&f+flBtb~Z^BnYI|s*>ntIUrvT9fEx>6Ze$% zBM87jM&*Fg3aF25KTdG%6S2k-3L0QOSo&b;EGrc?y;3RU*9=Y9>Uv>)&B(15R*S}} znJedO>*qeMJ+I$C#3T~$d8K`~z7sCq^BV7YjrY8Aqk)I-kdLIy+TIS-pkIcpi)Xae z*Mq2^aclTaQn+Tp(5S2U`Pxfv1=AB@a)1zq+7&0XOR-* zzU4*LKjNJ01pb18zpXoGgVLZ}ao57*Zlskdg-SErx8~Wa@ori;eB<4;bM2{@qsh(V z5+Sz7+FGxS!N}d`uEU)NX5q#Y^LxCTmPkGw2y{1k2&zg)9eF3!J#%8KIrlI6!3oJp zksK;swD9Eny?NV|+Y#@kb(3I4Lbz+lKH^;y#|{YTPY&+Qydo`RnZUq(%*23rH!TlE zy40Dq3CERwxgh|W$eRx1-LyV&j@!fPMjbrjP2%0OKK1w+SB;@HD?ONAw=x#g?|$}z zSjEx~fygYicsH#dV!LuB-c9R=X%O5~H#%T&#=B|p;?8(CEmuvn42*ZvVk34q*@kjB zl;tufme@HM@216G@W>QR%9)9G(?V2cr+=ukRPa3BO&jl~6-FP>x_vUZzB&AHN&_)>|qe{hiH*LI|mUOppc8Q!lNDZV!+!4z<5+ptV z=_aKU2R01HyJ9rKy%1tqplnlR@orlG zB}nmZ+NmA-G}64?uo15#!2V?K#W4X_q!Q!z>c_ijv zb1Qg7AN}G3eOuPUs>aW*tA<_BRtETTYqhw7jR|;MULm;EvWCBk zUa_4OWZ*khtG#kKI2=2jYFlet^1L>=iO+xH;~M})9|EN_V`Vb5t45(zSsB{);Q{p8 zQMKFsO^IPnVp!xD>K=yeoQh*k;An6hJ9xxVEJ+me0!5opGzx~U2^5Wv{Rq5r951We zR?yPQ-?A#eWPmLDx1*?2U0MLxKUKIBYqf^OivJCEb99gDV?r7e*lhd?BBKeg> z;^Lnr5~qHcIAQ+#MB;QJk^UdJhrjgy{477lKhKx~Vz3sZ%oRLID6DkxDsLMf>aE%a zIHz@suHWEk9+w3CqJ-~AndfRj3^~2f(WFq9q)>B(kn7qV3~*8idbv`u+y1yO3Mn>9 zou`Q^wY8K;oX}7Ft3>Mg#OXp=%e8Y|{dA#X6)ml#pDI`#!zkefKEh+^6$>YAqg*a` z=_F@W3gy$~cDYPL!4AJeqw35-laQsM|Ucx%bjA` z%6HLD*Mi>B@~1mmrD%0g#?Us24NG>dLB%eXcsRe5J88nQ~z9$l4qDHd-MKQ0>S2p!nWl{Pu z+D*<`cHuE_SV-Y-&UP13bYaf2ODOw^Im>eCSxhApNxaN0IDSaTH;03}Pb9nZ3W>~+N34OVX5V7h@PQ5}AG~1`FCJz#UY1Jl&Uy3A zjb?3i)2t`f8?`fk@trUK?vKCw#RRLJGxfLDco|V)e6{h`=AD~*t0nHXcr)Yd_`Uw( zZ+!Uuzdj+W{}EMxwU#!GW`jNvJz||6zQ2N#BWmHDn?|c89<;m%)X3`Z{qZ0E{(@}e z2h_;9+RJ8c=d1Lkk)!?gu+K5Lbu-s$;a*GL<5hq32jBg1Qda#Fs(P_@-ppTL$7?~I z5sz!}y0>rUTP^h_NB+-$^{+lJ>;8o5rfcBFi>FxXIhC%pWGz)&s4ddNMEcZdA(>2q z{@+R@|LhC=xG?MQr0qpCYgt{^T{b!TN!53izk9%gktx}Kb+c|CWA4ZzDq*ORld zgeS~_K)w7E)+@|YPZ%44{uQI^Npoa&|H{$zX7^9hvl3ko^d!?}COl&XAA<0NRi*aN zn863)AB?LAcmhvj20c-aY_W)XLe7j*bpL{Qs(hF5X3&%90!uFfo~Va)XQW(9GjMi2 zBf8#fc%px~8T1VFPa41{`S@$8?8wa|E4O0<=)TCS<1Oz)yw@fh=D!hq9PW!+_BI%GrN#(sw`lZb#vcEPMp_ z2q*hMqXLRoN(L055$OXyoldJI&E=W})XnYYM)MA9m~Y)Mx7SRTzJ6nMb7L)?O*if|n&wu^ebQPh<&B(CDYa^~ zT)mao@@uWydad57=j%1SwpvG9gmcB8plL-Q^E zs8va`oo%JSI(TO8Uf)HD2EJiI6ejL9*!sq9qnTMe&t5tE&Dth$m;;?vmDTEX=0j78 z`8QjY)ie``a!8qZ&`l)n@7V7oUxEWBmH4>ch5F$neiy1uDF*SoP|2u%o%JS+f7%4E zO&<2|(-%)r2l4r%wPe1ASZ<%k$#DM?K2>sHjm6uW`TJ?` zAe|BeRaKyNcepb?)&5T>%j3&zsY9r9{=RvHntHn0I&7o zZ0a1v??UBq6_;E}o{0Bm(%>t(C>u9^7izzWFJ*ZGNhExRbh*}ZM%D-$|L{#Uz@6t! zvH1AKRr&}}@Fk~w*HiG8*!C2>=OA%vgwLUdUVibUuaLdP({i0oo8u!?+3qwapo!L27>v~p}kdc7mn5r4p|KYwD%k_QA3la-+WSfZG z7WnAJ>Qa|^05T_~-N9@kvH!ak{@Hyr%57{#@c z*PfYc1-_Dt|7|*DB#!@!1e-YiAIJaW_&+B4IQ|cND>6?(jUUUX(Tu=qATBGD4b_0G zJ~I}`(-8yV9#PMYQbos)gYZ;5Wz;W@|0B<2nl8rduv;AekK_N8og}k~)rfo?|2N|J kzfj0eJN_@$|H}H?bpVun2SCoT3zc%Q`_z`venU(D52J tree.getRecord(x).element.toConstraints.resolveMinDimensions()) val widths = childDims.take(e.xDim).map(_._1) - val heights = childDims.grouped(e.xDim).map(_(0)._2).toSeq + val heights = childDims.take(e.xDim).map(_._2) Some(node.record.copy(element = SizedGrid( e.name, @@ -69,22 +70,249 @@ class ResolveConstraintsPass(topMod: String) extends Pass { // FIXME this is a hack that assumes all of the elements are equally constrained in a row (height) or column (width) // This is true for the current implementation of the constraints propagation but not generally true val childDims: Seq[(BigDecimal, BigDecimal)] = e.elements.map(x => tree.getRecord(x).element.toConstraints.resolveMinDimensions()) - val widths = childDims.take(e.xDim).map(_._1) - val heights = childDims.grouped(e.xDim).map(_(0)._2).toSeq + // This is a hack since ConstrainedElasticGrid is always 1D + val widths = childDims.take(e.yDim.max(e.xDim)).map(_._1) + val heights = childDims.take(e.yDim.max(e.xDim)).map(_._2) + println("+++++++++++: " , widths , "++++++++++++ \n") - Some(node.record.copy(element = SizedGrid( - e.name, - e.parent, - e.xDim, - e.yDim, - e.elements, - widths, - heights - ))) + // Optimization: Try to adjust aspect ratio if input constraints are violated + // PL represent node that only contains MemMacroArray + var is_PL = true + for (ele <- e.elements) { + tree.getNode(ele).record.element match { + case z: SizedGrid => + tree.getNode(z.elements(0)).record.element match { + case x: SizedMacro => + case x => is_PL = false + } + case z: SizedSpacerRect => + case z => is_PL = false + } + } + + // Indicate which modules are PL + println("is_PL: ", is_PL, " name of the module:", e.name) + + // Detect violations early + val hConstraint = e.height.getConstraint + val wConstraint = e.width.getConstraint + + val val_width = widths.reduce(_+_) + val val_height = heights.reduce(_+_) + println("Height Constraint: " + hConstraint + ", Width Constraint: " + wConstraint + "\n") + println("Actual Height: " + val_height + ", Actual Width: " + val_width + "\n") + if (hConstraint < val_height & wConstraint < val_width) { + throw new Exception("Module: " + e.name + " This module needs more space, try to give more height and width to the constraint.") + } else if (hConstraint < val_height) { + if (is_PL == false) { + throw new Exception("Module: " + e.name + " This module need more htight to be placed, try to give more height to the constraint.") + } else { + var height_space: BigDecimal = 0 + for (ele <- e.elements) { + tree.getNode(ele).record.element match { + case z: SizedSpacerRect => height_space = height_space + z.height + case z => + } + } + val new_height_cons = hConstraint - height_space + Some(node.record.copy(element = tryasp(new_height_cons, wConstraint, node, 1, 3, tree))) + } + + + } else if (wConstraint < val_width) { + if (is_PL == false) { + throw new Exception("Module: " + e.name + " This module needs more width, try to give more height to the constraint.") + } else { + printf("=========================calling the function on================" + e.elements + "\n") + var width_space: BigDecimal = 0 + node.children.foreach{cNode => + cNode.record.element match { + case z: SizedSpacerRect => width_space = width_space + z.width + case z => + } + } + for (ele <- e.elements) { + tree.getNode(ele).record.element match { + case z: ConstrainedSpacerRect => width_space = width_space + z.width.getConstraint; + case z => + } + } + val new_width_cons = wConstraint - width_space + Some(node.record.copy(element = tryasp(hConstraint, new_width_cons, node, 0, 3, tree))) + + } + + } else { + Some(node.record.copy(element = SizedGrid( + e.name, + e.parent, + e.xDim, + e.yDim, + e.elements, + widths, + heights + ))) + } case e => throw new Exception("Illegal element type") } } tree.toState } + def tryasp(height_constraint: BigDecimal, width_constraint: BigDecimal, elastic_array: FloorplanTreeNode, h_or_w: BigDecimal, ttl: BigDecimal, tree: FloorplanTree): SizedGrid = { + printf("==================Enter Tryasp===================\n") + if (ttl == 0) { + throw new Exception("Module: " + elastic_array.record.element.name + " Failed to change aspect ratio to resolve constraints. Please give more space to this module.") + } + + var memMacroArrays = elastic_array.children.filter(_.record.element.isInstanceOf[SizedGrid]) + + // helper function + def addSpacer(w: BigDecimal, h: BigDecimal, n: FloorplanTreeNode, e: SizedGrid): String = { + val newElement = SizedSpacerRect( + name = tree.getUniqueName("spacer"), + parent = e.name, + width = w, + height = h + ) + n.addChildRecord(FloorplanRecord(n.record.scope, None, None, newElement)) + newElement.name + } + + + // track new height and width + var new_height: BigDecimal = 0 + var new_width: BigDecimal = 0 + + for (arr <- memMacroArrays) { + val node = arr + val children = node.children + val e = node.record.element.asInstanceOf[SizedGrid] + + + var spacer_names = List.empty[String] + children.foreach{ child => + child.record.element match { + case SizedSpacerRect(_,_,_,_) => spacer_names = spacer_names :+ child.record.element.name + case _ => + } + } + for(name <- spacer_names) { + val nd = tree.getNode(name) + nd.delete() + e.elements.filterNot(_ == name) + } + + // for holding elements without spacers + var nonspacerElementHolder = Seq.empty[String] + for(ele <- e.elements) { + if (!spacer_names.contains(ele)) { + nonspacerElementHolder = nonspacerElementHolder :+ ele + } + } + + + // find out orignal aspect ratio + val old_aspect_ratio = e.height / e.width + var new_aspect_ratio: BigDecimal = 1 + if (h_or_w == 0) { + new_aspect_ratio = old_aspect_ratio * 1.1 + } else { + new_aspect_ratio = old_aspect_ratio * 0.9 + } + // Used for debugging aspect ratio + printf("===============new Width, Height, and Asp Ratio================" + e.width + " " + e.height + " " + new_aspect_ratio + "\n") + // construct new MemMacroArray + val mem0 = tree.getRecord(e.elements(0)).element.asInstanceOf[SizedMacro] + val numMems = node.children.length + // utilization is hard-coded maybe we can change this as well? + val utilization = 0.8 + val defaultWidth = (mem0.width * numMems) / utilization + val defaultHeight = mem0.height + val defaultArea = defaultWidth * defaultHeight + val height = BigDecimal(sqrt((defaultArea * new_aspect_ratio).doubleValue)) + val width = defaultArea / height + val stackHeight = (height / mem0.height).setScale(0, BigDecimal.RoundingMode.DOWN).toInt + val columns = (numMems + stackHeight - 1) / Seq(stackHeight, 1).max + val paddingColumns = (columns + 1) / 2 + val topPadding = height - (stackHeight*mem0.height) + val xPadding = (width - (columns*mem0.width)) / paddingColumns + val xDim = columns + paddingColumns + val yDim = stackHeight + 1 + + + // elastic_array is a TreeNode + if (topPadding < 0 || xPadding <= 0) { + throw new Exception("Module: " + elastic_array.record.element.name + " takes too much space, please adjust constraint accordingly.") + } + val elements = Seq.tabulate(xDim*yDim) { i => + val col = (i % xDim) + val row = (i / xDim) + val group = 2*(col / 3) + (row*columns) + val spacerHeight = if (row == yDim - 1) topPadding else mem0.height + if (col % 3 == 0) { + if (group >= numMems) { + addSpacer(xPadding, spacerHeight, node, e) + } else { + nonspacerElementHolder(group) + } + } else if (col % 3 == 1) { + addSpacer(xPadding, spacerHeight, node, e) + } else { + if ((group + 1) >= numMems) { + addSpacer(xPadding, spacerHeight, node, e) + } else { + val eOut = nonspacerElementHolder(group + 1) + val myNode = tree.getNode(eOut) + myNode.replace(myNode.record.copy(element = myNode.record.element.asInstanceOf[SizedMacro].copy( + orientation = Orientation.my + ))) + eOut + } + } + } + val widths = Seq.tabulate(xDim) { i => + if (i % 3 == 0) { + mem0.width + } else if (i % 3 == 1) { + xPadding + } else { + mem0.width + } + } + val heights = Seq.fill(stackHeight)(mem0.height) ++ Seq(topPadding) + val newElement = SizedGrid( + name = e.name, + parent = e.parent, + xDim = xDim, + yDim = yDim, + elements = elements, + widths = widths, + heights = heights + ) + node.replace(node.record.copy(element = newElement)) + new_height = new_height + heights.reduce(_+_) + new_width = new_width + widths.reduce(_+_) + } + // Resolve the resulf of change aspect ratio + if (new_height < height_constraint && new_width < width_constraint) { + val childDims: Seq[(BigDecimal, BigDecimal)] = elastic_array.record.element.asInstanceOf[ConstrainedElasticGrid].elements.map(x => tree.getRecord(x).element.toConstraints.resolveMinDimensions()) + val widths = childDims.take(elastic_array.record.element.asInstanceOf[ConstrainedElasticGrid].xDim).map(_._1) + val heights = childDims.take(elastic_array.record.element.asInstanceOf[ConstrainedElasticGrid].xDim).map(_._2) + val res = SizedGrid( + name = elastic_array.record.element.name, + parent = elastic_array.record.element.asInstanceOf[ConstrainedElasticGrid].parent, + xDim = elastic_array.record.element.asInstanceOf[ConstrainedElasticGrid].xDim, + yDim = elastic_array.record.element.asInstanceOf[ConstrainedElasticGrid].yDim, + elements = elastic_array.record.element.asInstanceOf[ConstrainedElasticGrid].elements, + widths = widths, + heights = heights + ) + res + + } else { + tryasp(height_constraint, width_constraint, elastic_array, h_or_w, ttl - 1, tree) + } + + } }