Skip to content

Commit

Permalink
Add public APIs for creating and dumping Profiles
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 committed Nov 11, 2024
1 parent 2b8f538 commit 3b702f6
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 3b702f6

Please sign in to comment.