Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UPF Generation #1347

Merged
merged 2 commits into from
Jul 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,8 @@ lazy val rocketchip = freshProject("rocketchip", rocketChipDir)
libraryDependencies ++= Seq(
"org.scala-lang" % "scala-reflect" % scalaVersion.value,
"org.json4s" %% "json4s-jackson" % "3.6.6",
"org.scalatest" %% "scalatest" % "3.2.0" % "test"
"org.scalatest" %% "scalatest" % "3.2.0" % "test",
"org.scala-graph" %% "graph-core" % "1.13.5"
)
)
.settings( // Settings for scalafix
Expand Down
9 changes: 8 additions & 1 deletion common.mk
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ HELP_COMPILATION_VARIABLES += \
" ENABLE_YOSYS_FLOW = if set, add compilation flags to enable the vlsi flow for yosys(tutorial flow)" \
" EXTRA_CHISEL_OPTIONS = additional options to pass to the Chisel compiler" \
" EXTRA_BASE_FIRRTL_OPTIONS = additional options to pass to the Scala FIRRTL compiler" \
" MFC_BASE_LOWERING_OPTIONS = override lowering options to pass to the MLIR FIRRTL compiler"
" MFC_BASE_LOWERING_OPTIONS = override lowering options to pass to the MLIR FIRRTL compiler" \
" ASPECTS = comma separated list of Chisel aspect flows to run (e.x. chipyard.upf.ChipTopUPFAspect)"

EXTRA_GENERATOR_REQS ?= $(BOOTROM_TARGETS)
EXTRA_SIM_CXXFLAGS ?=
Expand All @@ -29,6 +30,11 @@ EXTRA_SIM_SOURCES ?=
EXTRA_SIM_REQS ?=
ENABLE_CUSTOM_FIRRTL_PASS += $(ENABLE_YOSYS_FLOW)

ifneq ($(ASPECTS), )
comma = ,
ASPECT_ARGS = $(foreach aspect, $(subst $(comma), , $(ASPECTS)), --with-aspect $(aspect))
endif

#----------------------------------------------------------------------------
HELP_SIMULATION_VARIABLES += \
" EXTRA_SIM_FLAGS = additional runtime simulation flags (passed within +permissive)" \
Expand Down Expand Up @@ -134,6 +140,7 @@ $(FIRRTL_FILE) $(ANNO_FILE) $(CHISEL_LOG_FILE) &: $(CHIPYARD_CLASSPATH_TARGETS)
--name $(long_name) \
--top-module $(MODEL_PACKAGE).$(MODEL) \
--legacy-configs $(CONFIG_PACKAGE):$(CONFIG) \
$(ASPECT_ARGS) \
$(EXTRA_CHISEL_OPTIONS)) | tee $(CHISEL_LOG_FILE))

define mfc_extra_anno_contents
Expand Down
19 changes: 19 additions & 0 deletions docs/VLSI/Advanced-Usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,22 @@ With the Synopsys plugin, hierarchical RTL and gate-level simulation is supporte
* ``-$(VLSI_TOP)`` suffixes denote simulations/power analysis on a submodule in a hierarchical flow (remember to override this variable). Note that you must provide the testbenches for these modules since the default testbench only simulates a Chipyard-based ``ChipTop`` DUT instance.

The simulation configuration (e.g. binaries) can be edited for your design. See the ``Makefile`` and refer to Hammer's documentation for how to set up simulation parameters for your design.

UPF Generation Flow
sriramsridhar23 marked this conversation as resolved.
Show resolved Hide resolved
-------------------------------
This VLSI flow experimentally supports generating Chisel-based `UPF <https://vlsitutorials.com/upf-low-power-vlsi/>`__ files using `Chisel Aspects <https://javadoc.io/doc/edu.berkeley.cs/chisel3_2.13/latest/chisel3/aop/Aspect.html>`__.

To generate UPF for any design, first modify the ``UPFInputs`` object in ``generators/chipyard/src/main/scala/upf/UPFInputs.scala`` to fit your design power specifications.

This involves filling in the ``upfInfo`` list with ``PowerDomainInput`` objects representing all the power domains you want in your design, along with specifying hierarchy and domain attributes.

The given example in ``UPFInputs`` corresponds to a dual-core Rocket config with 3 power domains (1 parent domain with all uncore modules and 2 children corresponding to the Rocket tiles).

To run the flow:

.. code-block:: shell

cd chipyard/vlsi
make verilog ASPECTS=chipyard.upf.ChipTopUPFAspect

The output UPF files will be dumped in ``vlsi/generated-src/upf``.
sriramsridhar23 marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,5 @@ class TestHarness(implicit val p: Parameters) extends Module with HasHarnessInst
def referenceClock = clock
def referenceReset = reset

instantiateChipTops()
val lazyDuts = instantiateChipTops()
}
90 changes: 90 additions & 0 deletions generators/chipyard/src/main/scala/upf/ChipTopUPF.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// See LICENSE for license details
package chipyard.upf

import scala.collection.mutable.{ListBuffer}
import scalax.collection.mutable.{Graph}
import scalax.collection.GraphPredef._, scalax.collection.GraphEdge._

import chipyard.harness.{TestHarness}
import freechips.rocketchip.diplomacy.{LazyModule}

object ChipTopUPF {

def default: UPFFunc.UPFFunction = {
case top: LazyModule => {
val modulesList = getLazyModules(top)
val pdList = createPowerDomains(modulesList)
val g = connectPDHierarchy(pdList)
traverseGraph(g, UPFGenerator.generateUPF)
}
}

def getLazyModules(top: LazyModule): ListBuffer[LazyModule] = {
var i = 0
var result = new ListBuffer[LazyModule]()
result.append(top)
while (i < result.length) {
val lazyMod = result(i)
for (child <- lazyMod.getChildren) {
result.append(child)
}
i += 1
}
return result
}

def createPowerDomains(modulesList: ListBuffer[LazyModule]): ListBuffer[PowerDomain] = {
var pdList = ListBuffer[PowerDomain]()
for (pdInput <- UPFInputs.upfInfo) {
val pd = new PowerDomain(name=pdInput.name, modules=getPDModules(pdInput, modulesList),
isTop=pdInput.isTop, isGated=pdInput.isGated,
highVoltage=pdInput.highVoltage, lowVoltage=pdInput.lowVoltage)
pdList.append(pd)
}
return pdList
}

def getPDModules(pdInput: PowerDomainInput, modulesList: ListBuffer[LazyModule]): ListBuffer[LazyModule] = {
var pdModules = ListBuffer[LazyModule]()
for (moduleName <- pdInput.moduleList) {
var module = modulesList.filter(_.module.name == moduleName)
if (module.length == 1) { // filter returns a collection
pdModules.append(module(0))
} else {
module = modulesList.filter(_.module.instanceName == moduleName)
if (module.length == 1) {
pdModules.append(module(0))
} else {
module = modulesList.filter(_.module.pathName == moduleName)
if (module.length == 1) {
pdModules.append(module(0))
} else {
throw new Exception(s"PowerDomainInput module list doesn't exist in design.")
}
}
}
}
return pdModules
}

def connectPDHierarchy(pdList: ListBuffer[PowerDomain]): Graph[PowerDomain, DiEdge] = {
var g = Graph[PowerDomain, DiEdge]()
for (pd <- pdList) {
val pdInput = UPFInputs.upfInfo.filter(_.name == pd.name)(0)
val childPDs = pdList.filter(x => pdInput.childrenPDs.contains(x.name))
for (childPD <- childPDs) {
g += (pd ~> childPD) // directed edge from pd to childPD
}
}
return g
}

def traverseGraph(g: Graph[PowerDomain, DiEdge], action: (PowerDomain, Graph[PowerDomain, DiEdge]) => Unit): Unit = {
for (node <- g.nodes.filter(_.diPredecessors.isEmpty)) { // all nodes without parents
g.outerNodeTraverser(node).foreach(pd => action(pd, g))
}
}

}

case object ChipTopUPFAspect extends UPFAspect[chipyard.harness.TestHarness](ChipTopUPF.default)
24 changes: 24 additions & 0 deletions generators/chipyard/src/main/scala/upf/UPFAspect.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// See LICENSE for license details
sriramsridhar23 marked this conversation as resolved.
Show resolved Hide resolved
package chipyard.upf

import chisel3.aop.{Aspect}
import firrtl.{AnnotationSeq}
import chipyard.harness.{TestHarness}
import freechips.rocketchip.stage.phases.{TargetDirKey}
import freechips.rocketchip.diplomacy.{LazyModule}

abstract class UPFAspect[T <: TestHarness](upf: UPFFunc.UPFFunction) extends Aspect[T] {

final override def toAnnotation(top: T): AnnotationSeq = {
UPFFunc.UPFPath = top.p(TargetDirKey) + "/upf"
require(top.lazyDuts.length == 1) // currently only supports 1 chiptop
upf(top.lazyDuts.head)
AnnotationSeq(Seq()) // noop
}

}

object UPFFunc {
type UPFFunction = PartialFunction[LazyModule, Unit]
var UPFPath = "" // output dir path
}
Loading