diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 4a92efd41..db9106891 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -165,6 +165,13 @@ jobs: ./build/emu -i $WORKLOAD --diff $REF_SO make clean + - name: Difftest with Squash Batch GlobalEnable and Query + run: | + cd $NOOP_HOME + make emu MILL_ARGS="--difftest-config ESB" DIFFTEST_QUERY=1 -j2 + ./build/emu -i $WORKLOAD --diff $REF_SO + make clean + - name: Difftest with JsonProfile and DiffTestIOTrace run: | cd $NOOP_HOME @@ -237,10 +244,10 @@ jobs: ./build/simv +workload=$WORKLOAD +diff=$REF_SO make clean - - name: Verilator Build with VCS Top (with DutZone PerfCnt) + - name: Verilator Build with VCS Top (with DutZone PerfCnt Query) run: | cd $NOOP_HOME - make simv MILL_ARGS="--difftest-config ZP" DIFFTEST_PERFCNT=1 VCS=verilator -j2 + make simv MILL_ARGS="--difftest-config ZP" DIFFTEST_PERFCNT=1 DIFFTEST_QUERY=1 VCS=verilator -j2 ./build/simv +workload=$WORKLOAD +no-diff +max-cycles=100000 ./build/simv +workload=$WORKLOAD +diff=$REF_SO make clean @@ -261,10 +268,10 @@ jobs: ./build/simv +workload=$WORKLOAD +diff=$REF_SO make clean - - name: Verilator Build with VCS Top (with GlobalEnable Squash Replay Batch InternalStep NonBlock PerfCnt) + - name: Verilator Build with VCS Top (with GlobalEnable Squash Replay Batch InternalStep NonBlock PerfCnt Query) run: | cd $NOOP_HOME - make simv MILL_ARGS="--difftest-config ESRBINP" DIFFTEST_PERFCNT=1 VCS=verilator -j2 + make simv MILL_ARGS="--difftest-config ESRBINP" DIFFTEST_PERFCNT=1 DIFFTEST_QUERY=1 VCS=verilator -j2 ./build/simv +workload=$WORKLOAD +no-diff +max-cycles=100000 ./build/simv +workload=$WORKLOAD +diff=$REF_SO make clean diff --git a/Makefile b/Makefile index 1635e6b16..7aa14f6a8 100644 --- a/Makefile +++ b/Makefile @@ -94,6 +94,10 @@ SIM_CXXFLAGS += -I$(DIFFTEST_CSRC_DIR) ifeq ($(DIFFTEST_PERFCNT), 1) SIM_CXXFLAGS += -DCONFIG_DIFFTEST_PERFCNT endif +ifeq ($(DIFFTEST_QUERY), 1) +SIM_CXXFLAGS += -DCONFIG_DIFFTEST_QUERY +SIM_LDFLAGS += -lsqlite3 +endif endif # ChiselDB diff --git a/libso.mk b/libso.mk index 2f49afca0..172d1ece8 100644 --- a/libso.mk +++ b/libso.mk @@ -4,7 +4,7 @@ CC_OBJ_DIR = $(abspath $(BUILD_DIR)/cc_obj) LIB_CSRC_DIR = $(abspath ./src/test/csrc/vcs) LIB_CXXFILES = $(SIM_CXXFILES) $(shell find $(LIB_CSRC_DIR) -name "*.cpp") LIB_CXXFLAGS = -m64 -c -fPIC -g -std=c++11 -LIB_CXXFLAGS += $(subst \\\",\", $(SIM_CXXFLAGS)) -I$(LIB_CSRC_DIR) -DNUM_CORES=$(NUM_CORES) +LIB_CXXFLAGS += $(subst \\\",\", $(SIM_CXXFLAGS)) $(SIM_LDFLAGS) -I$(LIB_CSRC_DIR) -DNUM_CORES=$(NUM_CORES) ifeq ($(WITH_DRAMSIM3),1) LD_LIB = -L $(DRAMSIM3_HOME)/ -ldramsim3 -Wl,-rpath-link=$(DRAMSIM3_HOME)/libdramsim3.so diff --git a/palladium.mk b/palladium.mk index 081fc4fb9..df70a72e1 100644 --- a/palladium.mk +++ b/palladium.mk @@ -80,7 +80,7 @@ DPILIB_EMU = $(PLDM_BUILD_DIR)/libdpi_emu.so PLDM_CSRC_DIR = $(abspath ./src/test/csrc/vcs) PLDM_CXXFILES = $(SIM_CXXFILES) $(shell find $(PLDM_CSRC_DIR) -name "*.cpp") PLDM_CXXFLAGS = -m64 -c -fPIC -g -std=c++11 -I$(PLDM_IXCOM) -I$(PLDM_SIMTOOL) -PLDM_CXXFLAGS += $(subst \\\",\", $(SIM_CXXFLAGS)) -I$(PLDM_CSRC_DIR) -DNUM_CORES=$(NUM_CORES) +PLDM_CXXFLAGS += $(subst \\\",\", $(SIM_CXXFLAGS)) $(SIM_LDFLAGS) -I$(PLDM_CSRC_DIR) -DNUM_CORES=$(NUM_CORES) ifeq ($(WITH_DRAMSIM3),1) PLDM_LD_LIB = -L $(DRAMSIM3_HOME)/ -ldramsim3 -Wl,-rpath-link=$(DRAMSIM3_HOME)/libdramsim3.so diff --git a/src/main/scala/DPIC.scala b/src/main/scala/DPIC.scala index 93e8f4faa..773d2bdb2 100644 --- a/src/main/scala/DPIC.scala +++ b/src/main/scala/DPIC.scala @@ -23,6 +23,7 @@ import difftest._ import difftest.batch.{BatchInfo, BatchIO} import difftest.common.FileControl import difftest.gateway.{GatewayConfig, GatewayResult, GatewaySinkControl} +import difftest.util.Query import scala.collection.mutable.ListBuffer @@ -180,7 +181,13 @@ class DPIC[T <: DifftestBundle](gen: T, config: GatewayConfig) extends DPICBase( val body = lhs.zip(rhs.flatten).map { case (l, r) => s"packet->$l = $r;" } val packetDecl = Seq(getPacketDecl(gen, "io_", config)) val validAssign = if (!gen.bits.hasValid || gen.isFlatten) Seq() else Seq("packet->valid = true;") - packetDecl ++ validAssign ++ body + val query = + Seq(s""" + |#ifdef CONFIG_DIFFTEST_QUERY + | ${Query.writeInvoke(gen)} + |#endif // CONFIG_DIFFTEST_QUERY + |""".stripMargin) + packetDecl ++ validAssign ++ body ++ query } setInline(s"$desiredName.v", moduleBody) @@ -202,6 +209,12 @@ class DPICBatch(template: Seq[DifftestBundle], batchIO: BatchIO, config: Gateway unpack += getPacketDecl(gen, "", config) unpack += s"memcpy(packet, data, sizeof(${gen.desiredModuleName}));" unpack += s"data += ${elem_bytes.sum};" + unpack += + s""" + |#ifdef CONFIG_DIFFTEST_QUERY + | ${Query.writeInvoke(gen)} + |#endif // CONFIG_DIFFTEST_QUERY + |""".stripMargin unpack.toSeq.mkString("\n ") } @@ -260,6 +273,9 @@ class DPICBatch(template: Seq[DifftestBundle], batchIO: BatchIO, config: Gateway | } | else if (id == BatchInterval) { | dut_index = (dut_index + 1) % CONFIG_DIFFTEST_BATCH_SIZE; + |#ifdef CONFIG_DIFFTEST_QUERY + | difftest_query_step(); + |#endif // CONFIG_DIFFTEST_QUERY | continue; | } | $bundleAssign @@ -298,7 +314,9 @@ object DPIC { val interfaces = ListBuffer.empty[(String, String, String)] def apply(control: GatewaySinkControl, io: Valid[DifftestBundle], config: GatewayConfig): Unit = { - val module = Module(new DummyDPICWrapper(chiselTypeOf(io), config)) + val bundleType = chiselTypeOf(io) + Query.register(bundleType.bits, "io_") + val module = Module(new DummyDPICWrapper(bundleType, config)) module.control := control module.io := io val dpic = module.dpic @@ -309,6 +327,7 @@ object DPIC { } def batch(template: Seq[DifftestBundle], control: GatewaySinkControl, io: BatchIO, config: GatewayConfig): Unit = { + Query.register(template, "") val module = Module(new DummyDPICBatchWrapper(template, chiselTypeOf(io), config)) module.control := control module.io := io @@ -320,6 +339,7 @@ object DPIC { if (interfaces.isEmpty) { return GatewayResult() } + Query.collect() val interfaceCpp = ListBuffer.empty[String] interfaceCpp += "#ifndef __DIFFTEST_DPIC_H__" @@ -384,6 +404,7 @@ object DPIC { interfaceCpp += "" interfaceCpp += "#include \"difftest.h\"" interfaceCpp += "#include \"difftest-dpic.h\"" + interfaceCpp += "#include \"difftest-query.h\"" interfaceCpp += "" interfaceCpp += s""" diff --git a/src/main/scala/Difftest.scala b/src/main/scala/Difftest.scala index 1cfc426da..d06ea9f2d 100644 --- a/src/main/scala/Difftest.scala +++ b/src/main/scala/Difftest.scala @@ -679,6 +679,14 @@ object DifftestModule { |void diffstate_perfcnt_finish(long long msec); |#endif // CONFIG_DIFFTEST_PERFCNT |""".stripMargin + difftestCpp += + s""" + |#ifdef CONFIG_DIFFTEST_QUERY + |void difftest_query_init(); + |void difftest_query_step(); + |void difftest_query_finish(); + |#endif // CONFIG_DIFFTEST_QUERY + |""".stripMargin difftestCpp += "#endif // __DIFFSTATE_H__" difftestCpp += "" FileControl.write(difftestCpp, "diffstate.h") diff --git a/src/main/scala/util/Query.scala b/src/main/scala/util/Query.scala new file mode 100644 index 000000000..7b13de0c7 --- /dev/null +++ b/src/main/scala/util/Query.scala @@ -0,0 +1,113 @@ +/*************************************************************************************** + * Copyright (c) 2025 Beijing Institute of Open Source Chip (BOSC) + * Copyright (c) 2025 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 scala.collection.mutable.ListBuffer + +object Query { + private val tables = ListBuffer.empty[QueryTable] + def register(gen: DifftestBundle, locPrefix: String) = { + tables += new QueryTable(gen, locPrefix) + } + def register(gens: Seq[DifftestBundle], locPrefix: String) = { + gens.foreach { gen => tables += new QueryTable(gen, locPrefix) } + } + def writeInvoke(gen: DifftestBundle): String = { + tables.find(_.gen == gen).get.writeInvoke + } + def collect() = { + val queryCpp = ListBuffer.empty[String] + queryCpp += + s""" + |#ifndef __DIFFTEST_QUERY_H__ + |#define __DIFFTEST_QUERY_H__ + | + |#include + |#include "diffstate.h" + |#include "query.h" + | + |#ifdef CONFIG_DIFFTEST_QUERY + | + |class QueryStats: public QueryStatsBase { + |public: + | ${tables.map { t => s"Query* ${t.instName};" }.mkString("\n ")} + | QueryStats(char *path): QueryStatsBase(path) { + | ${tables.map(_.initInvoke).mkString("\n ")} + | } + | ${tables.map(_.initDecl).mkString("")} + | ${tables.map(_.writeDecl).mkString("")} + |}; + |#endif // CONFIG_DIFFTEST_QUERY + |#endif // __DIFFTEST_QUERY_H__ + |""".stripMargin + FileControl.write(queryCpp, "difftest-query.h") + } +} + +class QueryTable(val gen: DifftestBundle, locPrefix: String) { + val tableName: String = gen.desiredModuleName.replace("Difftest", "") + // Args: (key, value) + private val stepArgs: Seq[(String, String)] = Seq(("STEP", "query_step")) + private val locArgs: Seq[(String, String)] = { + val argList = ListBuffer.empty[(String, String)] + argList += (("COREID", "coreid")) + // resolve conflict with sql key + if (gen.isIndexed) argList += (("MY_INDEX", "index")) + if (gen.isFlatten) argList += (("ADDRESS", "address")) + argList.toSeq + } + private val dataArgs: Seq[(String, String)] = { + val dataPrefix = "packet->" + val argList = ListBuffer.empty[(String, String)] + for ((name, _, elem) <- gen.dataElements) { + val isRemoved = gen.isFlatten && Seq("valid", "address").contains(name) + if (!isRemoved) { + if (elem.length == 1) argList += ((name.toUpperCase, dataPrefix + name)) + else + Seq.tabulate(elem.length) { idx => + argList += (((name + s"_$idx").toUpperCase, dataPrefix + name + s"[$idx]")) + } + } + } + argList.toSeq + } + private val sqlArgs: Seq[(String, String)] = stepArgs ++ locArgs ++ dataArgs + + val instName: String = "query_" + tableName + val initDecl = + s""" + | void ${tableName}_init() { + | const char* createSql = \" CREATE TABLE $tableName(\" \\ + | "ID INTEGER PRIMARY KEY AUTOINCREMENT," \\ + | ${sqlArgs.map("\"" + _._1 + " INT NOT NULL").mkString("", ",\" \\\n ", ");\";")} + | const char* insertSql = \"INSERT INTO $tableName (${sqlArgs.map(_._1).mkString(",")}) \" \\ + | \" VALUES (${Seq.fill(sqlArgs.length) { "?" }.mkString(",")});\"; + | $instName = new Query(mem_db, createSql, insertSql); + | } + |""".stripMargin + val initInvoke = s"${tableName}_init();" + val writeDecl = + s""" + | void ${tableName}_write(${locArgs.map("uint8_t " + _._2).mkString(", ")}, ${gen.desiredModuleName}* packet) { + | query_${tableName}->write(${sqlArgs.length}, ${sqlArgs.map(_._2).mkString(", ")}); + | } + |""".stripMargin + val writeInvoke = s"qStats->${tableName}_write(${locArgs.map(locPrefix + _._2).mkString(", ")}, packet);" +} diff --git a/src/test/csrc/common/query.cpp b/src/test/csrc/common/query.cpp new file mode 100644 index 000000000..8c4ef2b6b --- /dev/null +++ b/src/test/csrc/common/query.cpp @@ -0,0 +1,43 @@ +/*************************************************************************************** +* Copyright (c) 2025 Beijing Institute of Open Source Chip (BOSC) +* Copyright (c) 2025 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. +***************************************************************************************/ + +#ifdef CONFIG_DIFFTEST_QUERY +#include "query.h" +#include "difftest-query.h" + +QueryStats *qStats; + +void difftest_query_init() { + char query_path[128]; + snprintf(query_path, 128, "%s/build/%s", getenv("NOOP_HOME"), "difftest_query.db"); + // remove exist file + FILE *fp = fopen(query_path, "r"); + if (fp) { + fclose(fp); + remove(query_path); + } + qStats = new QueryStats(query_path); +} + +void difftest_query_step() { + qStats->step(); +} + +void difftest_query_finish() { + delete qStats; +} + +#endif // CONFIG_DIFFTEST_QUERY diff --git a/src/test/csrc/common/query.h b/src/test/csrc/common/query.h new file mode 100644 index 000000000..136757287 --- /dev/null +++ b/src/test/csrc/common/query.h @@ -0,0 +1,104 @@ +/*************************************************************************************** +* Copyright (c) 2025 Beijing Institute of Open Source Chip (BOSC) +* Copyright (c) 2025 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. +***************************************************************************************/ + +#ifndef __QUERY_H__ +#define __QUERY_H__ + +#include "common.h" + +#ifdef CONFIG_DIFFTEST_QUERY +#include + +class Query { +protected: + sqlite3_stmt *pPrepare = nullptr; + sqlite3 *query_db = nullptr; + +public: + Query(sqlite3 *db, const char *createSql, const char *insertSql) { + query_db = db; + char *errMsg; + int rc; + rc = sqlite3_exec(query_db, createSql, 0, 0, &errMsg); + if (rc != SQLITE_OK) { + printf("SQL error: %s\n", errMsg); + assert(0); + } + rc = sqlite3_prepare_v2(query_db, insertSql, strlen(insertSql), &pPrepare, 0); + if (rc != SQLITE_OK) { + printf("SQL error: %s\n", sqlite3_errmsg(query_db)); + assert(0); + } + } + ~Query() { + sqlite3_finalize(pPrepare); + } + void write(int count, ...) { + va_list args; + va_start(args, count); + sqlite3_reset(pPrepare); + for (int i = 0; i < count; i++) { + sqlite3_bind_int(pPrepare, i + 1, va_arg(args, int)); + } + va_end(args); + sqlite3_step(pPrepare); + } +}; + +class QueryStatsBase { +public: + char path[128]; + long long query_step = 0; + sqlite3 *mem_db = nullptr; + QueryStatsBase(char *_path) { + strncpy(path, _path, 128); + sqlite3_open(":memory:", &mem_db); + sqlite3_exec(mem_db, "PRAGMA synchronous = OFF", 0, 0, 0); + sqlite3_exec(mem_db, "BEGIN;", 0, 0, 0); + } + ~QueryStatsBase() { + sqlite3_exec(mem_db, "COMMIT;", 0, 0, 0); + sqlite3 *disk_db = nullptr; + sqlite3_backup *pBackup; + int rc = sqlite3_open(path, &disk_db); + if (rc == SQLITE_OK) { + pBackup = sqlite3_backup_init(disk_db, "main", mem_db, "main"); + if (pBackup) { + (void)sqlite3_backup_step(pBackup, -1); + (void)sqlite3_backup_finish(pBackup); + } + rc = sqlite3_errcode(disk_db); + } + sqlite3_close(disk_db); + sqlite3_close(mem_db); + } + virtual void step() { + query_step++; + if (query_step % 10000 == 0) { + sqlite3_exec(mem_db, "COMMIT;", 0, 0, 0); + sqlite3_exec(mem_db, "BEGIN;", 0, 0, 0); + } + } +}; + +class QueryStats; +extern QueryStats *qStats; + +void difftest_query_init(); +void difftest_query_step(); +void difftest_query_finish(); +#endif // CONFIG_DIFFTEST_QUERY +#endif // __QUERY_H__ diff --git a/src/test/csrc/difftest/difftest.cpp b/src/test/csrc/difftest/difftest.cpp index 3dca1d8d9..78e7d07f5 100644 --- a/src/test/csrc/difftest/difftest.cpp +++ b/src/test/csrc/difftest/difftest.cpp @@ -27,6 +27,9 @@ #ifdef CONFIG_DIFFTEST_PERFCNT #include "perf.h" #endif // CONFIG_DIFFTEST_PERFCNT +#ifdef CONFIG_DIFFTEST_QUERY +#include "query.h" +#endif // CONFIG_DIFFTEST_QUERY Difftest **difftest = NULL; @@ -37,6 +40,9 @@ int difftest_init() { #ifdef CONFIG_DIFFTEST_IOTRACE difftest_iotrace_init(); #endif // CONFIG_DIFFTEST_IOTRACE +#ifdef CONFIG_DIFFTEST_QUERY + difftest_query_init(); +#endif // CONFIG_DIFFTEST_QUERY diffstate_buffer_init(); difftest = new Difftest *[NUM_CORES]; for (int i = 0; i < NUM_CORES; i++) { @@ -95,6 +101,9 @@ void difftest_set_dut() { } int difftest_step() { difftest_set_dut(); +#if defined(CONFIG_DIFFTEST_QUERY) && !defined(CONFIG_DIFFTEST_BATCH) + difftest_query_step(); +#endif // CONFIG_DIFFTEST_QUERY for (int i = 0; i < NUM_CORES; i++) { int ret = difftest[i]->step(); if (ret) { @@ -124,6 +133,9 @@ void difftest_finish() { #ifdef CONFIG_DIFFTEST_IOTRACE difftest_iotrace_free(); #endif // CONFIG_DIFFTEST_IOTRACE +#ifdef CONFIG_DIFFTEST_QUERY + difftest_query_finish(); +#endif // CONFIG_DIFFTEST_QUERY diffstate_buffer_free(); for (int i = 0; i < NUM_CORES; i++) { delete difftest[i];