From ea2f0933d7b443b11a31698ac7b45bba1d632ce4 Mon Sep 17 00:00:00 2001 From: Sriram Sridhar Date: Wed, 26 Apr 2023 15:14:44 -0700 Subject: [PATCH] UPF Generation [ci skip] --- build.sbt | 3 +- common.mk | 1 + .../src/main/scala/upf/ChipTopUPF.scala | 91 ++++++ .../src/main/scala/upf/UPFAspect.scala | 24 ++ .../chipyard/src/main/scala/upf/UPFGen.scala | 263 ++++++++++++++++++ .../src/main/scala/upf/UPFInputs.scala | 29 ++ vlsi/Makefile | 5 + 7 files changed, 415 insertions(+), 1 deletion(-) create mode 100644 generators/chipyard/src/main/scala/upf/ChipTopUPF.scala create mode 100644 generators/chipyard/src/main/scala/upf/UPFAspect.scala create mode 100644 generators/chipyard/src/main/scala/upf/UPFGen.scala create mode 100644 generators/chipyard/src/main/scala/upf/UPFInputs.scala diff --git a/build.sbt b/build.sbt index 8160a4f92e..9b66d69e46 100644 --- a/build.sbt +++ b/build.sbt @@ -110,7 +110,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 diff --git a/common.mk b/common.mk index a4668075dc..18c0e80f97 100644 --- a/common.mk +++ b/common.mk @@ -112,6 +112,7 @@ $(FIRRTL_FILE) $(ANNO_FILE) $(CHISEL_LOG_FILE) &: $(SCALA_SOURCES) $(SCALA_BUILD --name $(long_name) \ --top-module $(MODEL_PACKAGE).$(MODEL) \ --legacy-configs $(CONFIG_PACKAGE):$(CONFIG) \ + $(UPF_ASPECT) \ $(EXTRA_CHISEL_OPTIONS)) | tee $(CHISEL_LOG_FILE)) define mfc_extra_anno_contents diff --git a/generators/chipyard/src/main/scala/upf/ChipTopUPF.scala b/generators/chipyard/src/main/scala/upf/ChipTopUPF.scala new file mode 100644 index 0000000000..8d4c6b25dd --- /dev/null +++ b/generators/chipyard/src/main/scala/upf/ChipTopUPF.scala @@ -0,0 +1,91 @@ +// See LICENSE for license details +package chipyard.upf + +import chipyard.TestHarness +import freechips.rocketchip.diplomacy.LazyModule + +import scala.collection.mutable.ListBuffer + +import scalax.collection.mutable.Graph +import scalax.collection.GraphPredef._, scalax.collection.GraphEdge._ + +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.TestHarness](ChipTopUPF.default) \ No newline at end of file diff --git a/generators/chipyard/src/main/scala/upf/UPFAspect.scala b/generators/chipyard/src/main/scala/upf/UPFAspect.scala new file mode 100644 index 0000000000..7522adafac --- /dev/null +++ b/generators/chipyard/src/main/scala/upf/UPFAspect.scala @@ -0,0 +1,24 @@ +// See LICENSE for license details +package chipyard.upf + +import chisel3.aop.Aspect +import firrtl.{AnnotationSeq} +import chipyard.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" + upf(top.lazyDut) + AnnotationSeq(Seq()) // noop + } + +} + +object UPFFunc { + type UPFFunction = PartialFunction[LazyModule, Unit] + var UPFPath = "" // output dir path +} \ No newline at end of file diff --git a/generators/chipyard/src/main/scala/upf/UPFGen.scala b/generators/chipyard/src/main/scala/upf/UPFGen.scala new file mode 100644 index 0000000000..fafc42ac4f --- /dev/null +++ b/generators/chipyard/src/main/scala/upf/UPFGen.scala @@ -0,0 +1,263 @@ +package chipyard.upf + +import freechips.rocketchip.diplomacy.LazyModule + +import java.io.FileWriter +import java.nio.file.{Paths, Files} +import scala.collection.mutable.ListBuffer +import scalax.collection.mutable.Graph +import scalax.collection.GraphPredef._, scalax.collection.GraphEdge._ + +case class PowerDomain (val name: String, val modules: ListBuffer[LazyModule], + val isTop: Boolean, val isGated: Boolean, + val highVoltage: Double, val lowVoltage: Double) { + val mainVoltage = isGated match { + case true => highVoltage // gated nets should have access to high voltage rail (since they are being gated to optimize power) + case false => lowVoltage // currently assuming non-gated nets are on low voltage rail + } +} + +object UPFGenerator { + + def generateUPF(pd: PowerDomain, g: Graph[PowerDomain, DiEdge]): Unit = { + val node = g.get(pd) + val children = node.diSuccessors.map(x => x.toOuter).toList + val pdList = g.nodes.map(x => x.toOuter).toList + val filePath = UPFFunc.UPFPath + val fileName = s"${pd.name}.upf" + writeFile(filePath, fileName, createMessage(pd, children, pdList)) + } + + def createMessage(pd: PowerDomain, children: List[PowerDomain], pdList: List[PowerDomain]): String = { + var message = "" + message += loadUPF(pd, children) + message += createPowerDomains(pd) + message += createSupplyPorts(pd) + message += createSupplyNets(pd) + message += connectSupplies(pd) + message += setDomainNets(pd) + message += createPowerSwitches(pd) + message += createPowerStateTable(pd, getPorts(pd, children)) + message += createLevelShifters(pd, pdList) + return message + } + + def writeFile(filePath: String, fileName: String, message: String): Unit = { + if (!Files.exists(Paths.get(filePath))) { + Files.createDirectories(Paths.get(filePath)) + } + val fw = new FileWriter(s"${filePath}/${fileName}", false) + fw.write(message) + fw.close() + } + + def getPorts(pd: PowerDomain, children: List[PowerDomain]): ListBuffer[String] = { + var portsList = ListBuffer[String]() + portsList += "VDDH" + portsList += "VDDL" + if (pd.isGated) { + portsList += s"VDD_${pd.name}" + } + for (child <- children) { + if (child.isGated) { + portsList += s"VDD_${child.name}" + } + } + return portsList + } + + def loadUPF(pd: PowerDomain, children: List[PowerDomain]): String = { + var message = "##### Set Scope and Load UPF #####\n" + var subMessage = s"set_scope /${pd.modules(0).module.name}\n" // + children.foreach{ + child => { + subMessage += s"load_upf ${child.name}.upf -scope ${child.modules(0).module.name}\n" + } + } + message += subMessage + message += "\n" + return message + } + + def createPowerDomains(pd: PowerDomain): String = { + var message = "##### Create Power Domains #####\n" + var subMessage = "" + pd.isTop match { + case true => subMessage += s"create_power_domain ${pd.name} -include_scope\n" + case false => { + subMessage += s"create_power_domain ${pd.name} -elements { " + for (module <- pd.modules) { + subMessage += s"${module.module.name} " + } + subMessage += "}\n" + } + } + message += subMessage + message += "\n" + return message + } + + def createSupplyPorts(pd: PowerDomain): String = { + if (!pd.isTop) { + return "" + } + var message = "##### Create Supply Ports #####\n" + var subMessage = pd.isTop match { + case true => { + s"create_supply_port VDDH -direction in -domain ${pd.name}\n" + + s"create_supply_port VDDL -direction in -domain ${pd.name}\n" + + s"create_supply_port VSS -direction in -domain ${pd.name}\n" + } + case false => "" + } + message += subMessage + message += "\n" + return message + } + + def createSupplyNets(pd: PowerDomain): String = { + var message = "##### Create Supply Nets #####\n" + var subMessage = pd.isTop match { + case true => { + s"create_supply_net VDDH -domain ${pd.name}\n" + + s"create_supply_net VDDL -domain ${pd.name}\n" + + s"create_supply_net VSS -domain ${pd.name}\n" + } + case false => { + s"create_supply_net VDDH -domain ${pd.name} -reuse\n" + + s"create_supply_net VDDL -domain ${pd.name} -reuse\n" + + s"create_supply_net VSS -domain ${pd.name} -reuse\n" + } + } + if (pd.isGated) { + subMessage += s"create_supply_net VDD_${pd.name} -domain ${pd.name}\n" + } + message += subMessage + message += "\n" + return message + } + + def connectSupplies(pd: PowerDomain): String = { + var message = "##### Connect Supply Nets and Ports #####\n" + var subMessage = "connect_supply_net VDDH -ports VDDH\n" + + "connect_supply_net VDDL -ports VDDL\n" + + "connect_supply_net VSS -ports VSS\n" + message += subMessage + message += "\n" + return message + } + + def setDomainNets(pd: PowerDomain): String = { + var message = "##### Set Domain Supply Nets #####\n" + var subMessage = pd.isGated match { + case true => s"set_domain_supply_net ${pd.name} -primary_power_net VDD_${pd.name} -primary_ground_net VSS\n" + case false => s"set_domain_supply_net ${pd.name} -primary_power_net VDDL -primary_ground_net VSS\n" + } + message += subMessage + message += "\n" + return message + } + + def createPowerSwitches(pd: PowerDomain): String = { + if (!pd.isGated) { + return "" + } + var message = "##### Power Switches #####\n" + var subMessage = pd.isGated match { + case true => s"""create_power_switch sw_${pd.name} -domain ${pd.name} -input_supply_port "psw_VDDH VDDH" """ + + s"""-output_supply_port "psw_VDD_${pd.name} VDD_${pd.name}" """ + + s"""-control_port "psw_${pd.name}_en ${pd.modules(0).module.name}/${pd.modules(0).module.name}_en" """ + + s"""-on_state "psw_${pd.name}_ON psw_VDDH { !psw_${pd.name}_en }"""" + "\n" + case false => "" + } + message += subMessage + message += "\n" + return message + } + + def createPowerStateTable(pd: PowerDomain, portsList: ListBuffer[String]): String = { + if (!pd.isTop) { + return "" + } + var message = "##### Power State Table #####\n" + var portStates = "" + var createPST = "create_pst pst_table -supplies { " + + for (port <- portsList) { + createPST += s"${port} " + if (port == "VDDH") { + portStates += s"add_port_state ${port} -state { HighVoltage ${pd.highVoltage} }\n" + } else if (port == "VDDL") { + portStates += s"add_port_state ${port} -state { LowVoltage ${pd.lowVoltage} }\n" + } else { // gated + portStates += s"add_port_state ${port} -state { HighVoltage ${pd.highVoltage } -state { ${port}_OFF off }\n" + } + } + portStates += "\n" + createPST += "}\n\n" + + var pstStates = "" + for (state <- UPFInputs.states.keys) { + val stateVal = getStateVal(pd, state) + pstStates += s"add_pst_state ${state} -pst pst_table -state { " + for (port <- portsList) { + if (port == "VDDH") { + pstStates += s"HighVoltage " + } else if (port == "VDDL") { + pstStates += s"LowVoltage " + } else { // gated + stateVal match { + case 0 => pstStates += s"${port}_OFF " + case 1 => pstStates += s"HighVoltage " + } + } + } + pstStates += "}\n" + } + message += portStates + message += createPST + message += pstStates + message += "\n" + return message + } + + def getStateVal(pd: PowerDomain, state: String): Int = { + val stateVals = UPFInputs.states(state).split(",").map(_.trim.toInt) + val index = UPFInputs.domains.indexOf(pd.name) + return stateVals(index) + } + + // current strategy: for each power domain, create level shifters for outputs going to all other pds + // not creating level shifters for inputs since every pd will already shift its outputs + // creating level shifters going to every other pd since not sure how to check if there is communication or not between any 2 + def createLevelShifters(pd: PowerDomain, pdList: List[PowerDomain]): String = { + var message = "##### Level Shifters #####\n" + for (pd2 <- pdList) { + if (pd != pd2) { + val voltage1 = pd.mainVoltage + val voltage2 = pd2.mainVoltage + var subMessage = voltage1 match { + case x if x < voltage2 => { + s"set_level_shifter LtoH_${pd.name}_to_${pd2.name} " + + s"-domain ${pd.name} " + + "-applies_to outputs " + + "rule low_to_high " + + "-location self\n" + } + case y if y > voltage2 => { + s"set_level_shifter HtoL_${pd.name}_to_${pd2.name} " + + s"-domain ${pd.name} " + + "-applies_to outputs " + + "rule high_to_low " + + "-location self\n" + } + case _ => "" + } + message += subMessage + } + } + message += "\n" + return message + } + +} \ No newline at end of file diff --git a/generators/chipyard/src/main/scala/upf/UPFInputs.scala b/generators/chipyard/src/main/scala/upf/UPFInputs.scala new file mode 100644 index 0000000000..31a04451a5 --- /dev/null +++ b/generators/chipyard/src/main/scala/upf/UPFInputs.scala @@ -0,0 +1,29 @@ +package chipyard.upf + + +object UPFInputs { + + val upfInfo = List( + PowerDomainInput(name="PD_top", isTop=true, moduleList=List("DigitalTop"), + parentPD="", childrenPDs=List("PD_RocketTile1", "PD_RocketTile2"), + isGated=false, highVoltage=3.9, lowVoltage=3.4), + PowerDomainInput(name="PD_RocketTile1", isTop=false, moduleList=List("RocketTile"), + parentPD="PD_top", childrenPDs=List(), + isGated=false, highVoltage=3.9, lowVoltage=3.1), + PowerDomainInput(name="PD_RocketTile2", isTop=false, moduleList=List("RocketTile_1"), + parentPD="PD_top", childrenPDs=List(), + isGated=false, highVoltage=3.9, lowVoltage=3.2), + ) + + // PST info + val domains = List("PD_top", "PD_RocketTile1", "PD_RocketTile2") + val states = Map( + "ON" -> "1, 1, 1", + "OFF" -> "0, 0, 0" + ) + +} + +case class PowerDomainInput(name: String, isTop: Boolean, moduleList: List[String], + parentPD: String, childrenPDs: List[String], + isGated: Boolean, highVoltage: Double, lowVoltage: Double) diff --git a/vlsi/Makefile b/vlsi/Makefile index 8259e7a6ec..2a77eced0e 100644 --- a/vlsi/Makefile +++ b/vlsi/Makefile @@ -51,6 +51,11 @@ else OBJ_DIR ?= $(vlsi_dir)/$(VLSI_OBJ_DIR)/$(long_name)-$(TOP) endif +ifneq ($(ASPECTS), ) + comma = , + UPF_ASPECT = $(foreach aspect, $(subst $(comma), , $(ASPECTS)), --with-aspect $(aspect)) +endif + ######################################################################################### # general rules #########################################################################################