diff --git a/src/main/scala/Difftest.scala b/src/main/scala/Difftest.scala index 8e6a095f2..2941544b0 100644 --- a/src/main/scala/Difftest.scala +++ b/src/main/scala/Difftest.scala @@ -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 @@ -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. @@ -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 { @@ -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) { @@ -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) { @@ -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 @@ -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 } @@ -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) { @@ -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 diff --git a/src/main/scala/util/Profile.scala b/src/main/scala/util/Profile.scala new file mode 100644 index 000000000..ed7263a8c --- /dev/null +++ b/src/main/scala/util/Profile.scala @@ -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") + } +} diff --git a/src/test/scala/DifftestTop.scala b/src/test/scala/DifftestTop.scala index 62ccdb205..b9a96735e 100644 --- a/src/test/scala/DifftestTop.scala +++ b/src/test/scala/DifftestTop.scala @@ -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 { @@ -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() @@ -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)