Skip to content

Commit

Permalink
Add public APIs for creating and dumping Profiles (#496)
Browse files Browse the repository at this point in the history
These functions have been implemented before. This commit organizes
them together to a separate Scala file.
  • Loading branch information
poemonsense authored Nov 11, 2024
1 parent 2b8f538 commit e4bfcfd
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 41 deletions.
23 changes: 9 additions & 14 deletions src/main/scala/Difftest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@ import chisel3.reflect.DataMirror
import chisel3.util._
import difftest.common.{DifftestWiring, FileControl}
import difftest.gateway.{Gateway, GatewayConfig}
import org.json4s.DefaultFormats
import org.json4s.native.Serialization.writePretty
import difftest.util.Profile

import scala.annotation.tailrec
import scala.collection.mutable.ListBuffer
Expand Down Expand Up @@ -132,7 +131,8 @@ sealed trait DifftestBundle extends Bundle with DifftestWithCoreid { this: Difft
cpp.mkString("\n")
}

def toJsonProfile: Map[String, Any] = Map("className" -> this.getClass.getName)
// TODO: this should be implemented using reflection.
def classArgs: Map[String, Any] = Map()

// returns a Seq indicating the udpate dependencies. Default: empty
// Only when one of the dependencies is valid, this bundle is updated.
Expand Down Expand Up @@ -241,7 +241,7 @@ class DiffInstrCommit(nPhyRegs: Int = 32) extends InstrCommit(nPhyRegs) with Dif
squashed
}

override def toJsonProfile: Map[String, Any] = super.toJsonProfile ++ Map("nPhyRegs" -> nPhyRegs)
override def classArgs: Map[String, Any] = Map("nPhyRegs" -> nPhyRegs)
}

class DiffCommitData extends CommitData with DifftestBundle with DifftestWithIndex {
Expand Down Expand Up @@ -282,7 +282,7 @@ class DiffIntWriteback(numRegs: Int = 32) extends DataWriteback(numRegs) with Di
override protected val needFlatten: Boolean = true
// It is required for MMIO/Load(only for multi-core) data synchronization, and commit instr trace record
override def supportsSquashBase: Bool = true.B
override def toJsonProfile: Map[String, Any] = super.toJsonProfile ++ Map("numRegs" -> numRegs)
override def classArgs: Map[String, Any] = Map("numRegs" -> numRegs)
}

class DiffFpWriteback(numRegs: Int = 32) extends DiffIntWriteback(numRegs) {
Expand All @@ -303,7 +303,7 @@ abstract class DiffArchDelayedUpdate(numRegs: Int)
extends ArchDelayedUpdate(numRegs)
with DifftestBundle
with DifftestWithIndex {
override def toJsonProfile: Map[String, Any] = super.toJsonProfile ++ Map("numRegs" -> numRegs)
override def classArgs: Map[String, Any] = Map("numRegs" -> numRegs)
}

class DiffArchIntDelayedUpdate extends DiffArchDelayedUpdate(32) {
Expand Down Expand Up @@ -456,7 +456,7 @@ object DifftestModule {
private val instances = ListBuffer.empty[DifftestBundle]
private val cppMacros = ListBuffer.empty[String]
private val vMacros = ListBuffer.empty[String]
private val jsonProfiles = ListBuffer.empty[Map[String, Any]]
private val interfaces = ListBuffer.empty[(DifftestBundle, Int)]

def parseArgs(args: Array[String]): Array[String] = {
@tailrec
Expand Down Expand Up @@ -485,7 +485,7 @@ object DifftestModule {
difftest := DontCare
difftest.bits.getValidOption.foreach(_ := false.B)
}
jsonProfiles += (gen.toJsonProfile ++ Map("delay" -> delay))
interfaces.append((gen, delay))
difftest
}

Expand All @@ -497,7 +497,7 @@ object DifftestModule {

generateCppHeader(cpu, gateway.structPacked.getOrElse(false))
generateVeriogHeader()
generateJsonProfile(cpu)
Profile.generateJson(cpu, interfaces.toSeq)

Option.when(createTopIO) {
if (enabled) {
Expand Down Expand Up @@ -664,11 +664,6 @@ object DifftestModule {
}

def generateVeriogHeader(): Unit = FileControl.write(vMacros.map(m => s"`define $m"), "DifftestMacros.v")

def generateJsonProfile(cpu: String): Unit = {
val profile = jsonProfiles ++ Map("cpu" -> cpu)
FileControl.write(Seq(writePretty(profile)(DefaultFormats)), "difftest_profile.json")
}
}

// Difftest emulator top. Will be created by DifftestModule.finish
Expand Down
87 changes: 87 additions & 0 deletions src/main/scala/util/Profile.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/***************************************************************************************
* Copyright (c) 2024 Institute of Computing Technology, Chinese Academy of Sciences
*
* DiffTest is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*
* See the Mulan PSL v2 for more details.
***************************************************************************************/

package difftest.util

import difftest.DifftestBundle
import difftest.common.FileControl
import org.json4s.DefaultFormats
import org.json4s.native._

import java.nio.file.{Files, Paths}

// Note: json4s seems to load integers with BigInt. We may need to convert them into int if necessary

case class BundleProfile(
className: String,
classArgs: Map[String, Any],
delay: Int,
) {
def toBundle: DifftestBundle = {
val constructor = Class.forName(className).getConstructors()(0)
val args = constructor.getParameters.map { param =>
(classArgs(param.getName), param.getType.getName) match {
case (arg: BigInt, "int") => arg.toInt
case (arg, _) => arg
}
}
constructor.newInstance(args: _*).asInstanceOf[DifftestBundle]
}
}

case class DifftestProfile(
cpu: String,
numCores: Int,
bundles: Seq[BundleProfile],
) {
def toJsonString: String = Serialization.writePretty(this)(DefaultFormats)
}

object DifftestProfile {
def fromBundles(cpu: String, bundles: Seq[(DifftestBundle, Int)]): DifftestProfile = {
val numCores = bundles.count(_._1.isUniqueIdentifier)
require(bundles.length % numCores == 0, "cannot create the profile if cores are not symmetric")
val bundleProfiles = bundles.take(bundles.length / numCores).map { case (b, d) => BundleProfile.fromBundle(b, d) }
DifftestProfile(cpu, numCores, bundleProfiles)
}

def fromJson(filename: String): DifftestProfile = {
val profileStr = new String(Files.readAllBytes(Paths.get(filename)))
val profiles = JsonMethods.parse(profileStr).extract(DefaultFormats, manifest[Map[String, Any]])
val cpu = profiles("cpu").asInstanceOf[String]
val numCores = profiles("numCores").asInstanceOf[BigInt].toInt
val bundles = profiles("bundles").asInstanceOf[List[Map[String, Any]]].map { bundleProfileMap =>
BundleProfile(
bundleProfileMap("className").asInstanceOf[String],
bundleProfileMap("classArgs").asInstanceOf[Map[String, Any]],
bundleProfileMap("delay").asInstanceOf[BigInt].toInt,
)
}
DifftestProfile(cpu, numCores, bundles)
}
}

object BundleProfile {
def fromBundle(bundle: DifftestBundle, delay: Int): BundleProfile = {
BundleProfile(bundle.getClass.getName, bundle.classArgs, delay)
}
}

object Profile {
def generateJson(cpu: String, bundles: Seq[(DifftestBundle, Int)], profileName: String = "difftest"): Unit = {
val difftestProfile = DifftestProfile.fromBundles(cpu, bundles)
FileControl.write(Seq(difftestProfile.toJsonString), s"${profileName}_profile.json")
}
}
39 changes: 12 additions & 27 deletions src/test/scala/DifftestTop.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,9 @@
package difftest

import chisel3._
import difftest.util.DifftestProfile

import java.nio.file.{Files, Paths}
import scala.annotation.tailrec
import scala.collection.mutable.ListBuffer
import org.json4s.DefaultFormats
import org.json4s.native.JsonMethods.parse

// Main class to generate difftest modules when design is not written in chisel.
class DifftestTop extends Module {
Expand Down Expand Up @@ -65,34 +62,22 @@ class DifftestTop extends Module {
}

// Generate simulation interface based on Profile describing the instantiated information of design
class SimTop(profileName: String, numCores: Int) extends Module {
val instances = ListBuffer.empty[DifftestBundle]
val profileStr = new String(Files.readAllBytes(Paths.get(profileName)))
val profiles = parse(profileStr).extract[List[Map[String, Any]]](DefaultFormats, manifest[List[Map[String, Any]]])
for (coreid <- 0 until numCores) {
profiles.filter(_.contains("className")).zipWithIndex.foreach { case (rawProf, idx) =>
val prof = rawProf.map { case (k, v) =>
v match {
case i: BigInt => (k, i.toInt) // convert BigInt to Int
case x => (k, x)
}
}
val constructor = Class.forName(prof("className").toString).getConstructors()(0)
val args = constructor.getParameters().toSeq.map { param => prof(param.getName.toString) }
val inst = constructor.newInstance(args: _*).asInstanceOf[DifftestBundle]
instances += inst
DifftestModule(inst, true, prof("delay").asInstanceOf[Int]).suggestName(s"gateway_${coreid}_$idx")
class SimTop(profileName: String, numCoresOption: Option[Int]) extends Module {
val profile = DifftestProfile.fromJson(profileName)
val numCores = numCoresOption.getOrElse(profile.numCores)
val bundles = (0 until numCores).flatMap(coreid =>
profile.bundles.zipWithIndex.map { case (p, i) =>
DifftestModule(p.toBundle, true, p.delay).suggestName(s"gateway_${coreid}_$i")
}
}
val dutInfo = profiles.find(_.contains("cpu")).get
DifftestModule.generateSvhInterface(instances.toSeq, numCores)
DifftestModule.finish(dutInfo("cpu").asInstanceOf[String])
)
DifftestModule.generateSvhInterface(bundles, numCores)
DifftestModule.finish(profile.cpu)
}

abstract class DifftestApp extends App {
case class GenParams(
profile: Option[String] = None,
numCores: Int = 1,
numCores: Option[Int] = None,
)
def parseArgs(args: Array[String]): (GenParams, Array[String]) = {
val default = new GenParams()
Expand All @@ -102,7 +87,7 @@ abstract class DifftestApp extends App {
list match {
case Nil => param
case "--profile" :: str :: tail => nextOption(param.copy(profile = Some(str)), tail)
case "--num-cores" :: value :: tail => nextOption(param.copy(numCores = value.toInt), tail)
case "--num-cores" :: value :: tail => nextOption(param.copy(numCores = Some(value.toInt)), tail)
case option :: tail =>
firrtlOpts :+= option
nextOption(param, tail)
Expand Down

0 comments on commit e4bfcfd

Please sign in to comment.