Skip to content

Commit

Permalink
Profile: add public APIs for creating and dumping
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 8, 2024
1 parent 2b8f538 commit bacbfb3
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 48 deletions.
28 changes: 10 additions & 18 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] = super.classArgs ++ 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] = super.classArgs ++ 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] = super.classArgs ++ Map("numRegs" -> numRegs)
}

class DiffArchIntDelayedUpdate extends DiffArchDelayedUpdate(32) {
Expand Down Expand Up @@ -453,10 +453,8 @@ trait DifftestModule[T <: DifftestBundle] {

object DifftestModule {
private val enabled = true
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]]

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

def finish(cpu: String, createTopIO: Boolean): Option[DifftestTopIO] = {
val gateway = Gateway.collect()
cppMacros ++= gateway.cppMacros
vMacros ++= gateway.vMacros
instances ++= gateway.instances
val instances = gateway.instances

generateCppHeader(cpu, gateway.structPacked.getOrElse(false))
generateCppHeader(cpu, instances.map(_._1), gateway.structPacked.getOrElse(false))
generateVeriogHeader()
generateJsonProfile(cpu)
Profile.generateJson(cpu, instances)

Option.when(createTopIO) {
if (enabled) {
Expand Down Expand Up @@ -579,7 +576,7 @@ object DifftestModule {
FileControl.write(difftestSvh, "gateway_interface.svh")
}

def generateCppHeader(cpu: String, structPacked: Boolean): Unit = {
def generateCppHeader(cpu: String, instances: Seq[DifftestBundle], structPacked: Boolean): Unit = {
val difftestCpp = ListBuffer.empty[String]
difftestCpp += "#ifndef __DIFFSTATE_H__"
difftestCpp += "#define __DIFFSTATE_H__"
Expand Down Expand Up @@ -664,11 +661,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
6 changes: 3 additions & 3 deletions src/main/scala/Gateway.scala
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ case class GatewayConfig(
case class GatewayResult(
cppMacros: Seq[String] = Seq(),
vMacros: Seq[String] = Seq(),
instances: Seq[DifftestBundle] = Seq(),
instances: Seq[(DifftestBundle, Int)] = Seq(),
structPacked: Option[Boolean] = None,
exit: Option[UInt] = None,
step: Option[UInt] = None,
Expand Down Expand Up @@ -177,12 +177,12 @@ object Gateway {
val endpoint = Module(new GatewayEndpoint(instanceWithDelay.toSeq, config))
endpoint.in := gatewayIn
GatewayResult(
instances = endpoint.instances,
instances = endpoint.instances.map(i => (i, 0)),
structPacked = Some(config.isBatch),
step = Some(endpoint.step),
)
} else {
GatewayResult(instances = instances) + GatewaySink.collect(config)
GatewayResult(instances = instanceWithDelay.toSeq) + GatewaySink.collect(config)
}
sink + GatewayResult(
cppMacros = config.cppMacros,
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 bacbfb3

Please sign in to comment.