Batch: pack Batch param to facilitate migration between DPIC/PCIe (#474)

To facilitate Batch migration between DPIC and PCIe, we pack Batch
param to an aligned array, and parse it inside software.

In transmission, we only need to pass single hardware data, as
svBitVecVal[] or uint8_t[], and then view it as generated struct in
software.

Note we put also step inside BatchInfo and call simv_nstep inside,
Step will be also exposed to Top all the time for Verilator and
Timeout Check.

We also add isFPGA to GatewayConfig and generate BYTELEN macro
for FPGA.

Co-authored-by: Kami <fengkehan@bosc.ac.cn>
This commit is contained in:
Kunlin You 2024-09-25 14:30:15 +08:00 committed by GitHub
parent 4245818be8
commit fbd72a2e71
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 61 additions and 55 deletions

View File

@ -240,7 +240,7 @@ jobs:
make difftest_verilog PROFILE=../build/generated-src/difftest_profile.json NUMCORES=1 CONFIG=EL MFC=1
make emu WITH_CHISELDB=0 WITH_CONSTANTIN=0 IOTRACE_ZSTD=1 -j2
./build/emu -b 0 -e 0 -i ../ready-to-run/microbench.bin --diff ../ready-to-run/riscv64-nemu-interpreter-so --iotrace-name ../iotrace
test-difftest-fuzzing:
# This test runs on ubuntu-20.04 for two reasons:
# (1) riscv-arch-test can be built with riscv-linux-gnu toolchain 9.4.0,
@ -389,14 +389,14 @@ jobs:
./build/simv +workload=./ready-to-run/microbench.bin +e=0 +no-diff +max-cycles=100000
./build/simv +workload=./ready-to-run/microbench.bin +e=0 +diff=./ready-to-run/riscv64-nemu-interpreter-so
- name: Verilator Build with VCS Top (with DutZone GlobalEnable Squash Replay Batch PerfCnt)
- name: Verilator Build with VCS Top (with GlobalEnable Squash Replay Batch PerfCnt)
run: |
cd $GITHUB_WORKSPACE/../xs-env
source ./env.sh
cd $GITHUB_WORKSPACE/../xs-env/NutShell
source ./env.sh
make clean
make simv MILL_ARGS="--difftest-config ZESRBP" DIFFTEST_PERFCNT=1 VCS=verilator -j2
make simv MILL_ARGS="--difftest-config ESRBP" DIFFTEST_PERFCNT=1 VCS=verilator -j2
./build/simv +workload=./ready-to-run/microbench.bin +e=0 +no-diff +max-cycles=100000
./build/simv +workload=./ready-to-run/microbench.bin +e=0 +diff=./ready-to-run/riscv64-nemu-interpreter-so

View File

@ -41,8 +41,8 @@ class BatchIO(dataType: UInt, infoType: UInt) extends Bundle {
val info = infoType
}
class BatchOutput(data: UInt, info: UInt, config: GatewayConfig) extends Bundle {
val io = new BatchIO(chiselTypeOf(data), chiselTypeOf(info))
class BatchOutput(dataType: UInt, infoType: UInt, config: GatewayConfig) extends Bundle {
val io = new BatchIO(dataType, infoType)
val enable = Bool()
val step = UInt(config.stepWidth.W)
}
@ -131,9 +131,7 @@ class BatchEndpoint(bundles: Seq[Valid[DifftestBundle]], config: GatewayConfig,
}
val BatchInterval = WireInit(0.U.asTypeOf(new BatchInfo))
val BatchFinish = WireInit(0.U.asTypeOf(new BatchInfo))
BatchInterval.id := Batch.getTemplate.length.U
BatchFinish.id := (Batch.getTemplate.length + 1).U
val step_data = dataCollect_vec.last
val step_info = Cat(infoCollect_vec.last, BatchInterval.asUInt)
val step_data_len = dataLenCollect_vec.last
@ -188,7 +186,10 @@ class BatchEndpoint(bundles: Seq[Valid[DifftestBundle]], config: GatewayConfig,
}
}
val out = IO(Output(new BatchOutput(state_data, state_info, config)))
val BatchFinish = Wire(new BatchInfo)
BatchFinish.id := (Batch.getTemplate.length + 1).U
BatchFinish.num := state_step_cnt
val out = IO(Output(new BatchOutput(chiselTypeOf(state_data), chiselTypeOf(state_info), config)))
out.io.data := state_data
out.io.info := state_info | BatchFinish.asUInt << (state_info_len << 3)
out.enable := should_tick

View File

@ -30,23 +30,28 @@ abstract class DPICBase(config: GatewayConfig) extends ExtModule with HasExtModu
val clock = IO(Input(Clock()))
val enable = IO(Input(Bool()))
val dut_zone = Option.when(config.hasDutZone)(IO(Input(UInt(config.dutZoneWidth.W))))
val step = Option.when(config.hasInternalStep)(IO(Input(UInt(config.stepWidth.W))))
def getDirectionString(data: Data): String = {
if (DataMirror.directionOf(data) == ActualDirection.Input) "input " else "output"
}
def getDPICArgString(argName: String, data: Data, isC: Boolean): String = {
def getDPICArgString(argName: String, data: Data, isC: Boolean, isDPIC: Boolean = true): String = {
val typeString = data.getWidth match {
case 1 => if (isC) "uint8_t" else "bit"
case width if width > 1 && width <= 8 => if (isC) "uint8_t" else "byte"
case width if width > 8 && width <= 32 => if (isC) "uint32_t" else "int"
case width if width > 32 && width <= 64 => if (isC) "uint64_t" else "longint"
case width if width > 64 => if (isC) "const svBitVecVal" else s"bit[${width - 1}:0]"
case width if width > 64 =>
if (isC)
if (isDPIC) "const svBitVecVal" else "uint8_t"
else s"bit[${width - 1}:0]"
}
if (isC) {
val width = data.getWidth
if (width > 64) f"$typeString $argName[${width / 32}]" else f"$typeString%-8s $argName"
val suffix = if (width > 64) {
if (isDPIC) s"[${(width + 31) / 32}]" else s"[${(width + 7) / 8}]"
} else ""
f"$typeString%-8s $argName$suffix"
} else {
val directionString = getDirectionString(data)
f"$directionString $typeString%8s $argName"
@ -63,7 +68,6 @@ abstract class DPICBase(config: GatewayConfig) extends ExtModule with HasExtModu
def modPorts: Seq[Seq[(String, Data)]] = {
var ports = commonPorts
if (config.hasDutZone) ports ++= Seq(("dut_zone", dut_zone.get))
if (config.hasInternalStep) ports ++= Seq(("step", step.get))
ports.map(Seq(_))
}
@ -74,7 +78,7 @@ abstract class DPICBase(config: GatewayConfig) extends ExtModule with HasExtModu
def dpicFuncProto: String =
s"""
|extern "C" void $dpicFuncName (
| ${dpicFuncArgs.flatten.map(arg => getDPICArgString(arg._1, arg._2, true)).mkString(",\n ")}
| ${dpicFuncArgs.flatten.map(arg => getDPICArgString(arg._1, arg._2, true, !config.isFPGA)).mkString(",\n ")}
|)""".stripMargin
def getPacketDecl(gen: DifftestBundle, prefix: String, config: GatewayConfig): String = {
val dut_zone = if (config.hasDutZone) "dut_zone" else "0"
@ -94,20 +98,12 @@ abstract class DPICBase(config: GatewayConfig) extends ExtModule with HasExtModu
|""".stripMargin
}
def internalStep: String = if (config.hasInternalStep)
"""
|extern void simv_nstep(uint8_t step);
|simv_nstep(step);
|""".stripMargin
else ""
def dpicFunc: String =
s"""
|$dpicFuncProto {
| if (!diffstate_buffer) return;
|$perfCnt
| ${dpicFuncAssigns.mkString("\n ")}
| $internalStep
|}
|""".stripMargin
@ -192,7 +188,7 @@ class DPIC[T <: DifftestBundle](gen: T, config: GatewayConfig) extends DPICBase(
}
class DPICBatch(template: Seq[DifftestBundle], batchIO: BatchIO, config: GatewayConfig) extends DPICBase(config) {
val io = IO(Input(batchIO))
val io = IO(Input(UInt(batchIO.getWidth.W)))
def getDPICBundleUnpack(gen: DifftestBundle): String = {
val unpack = ListBuffer.empty[String]
@ -210,7 +206,7 @@ class DPICBatch(template: Seq[DifftestBundle], batchIO: BatchIO, config: Gateway
unpack.toSeq.mkString("\n ")
}
override def modPorts = super.modPorts ++ Seq(Seq(("io_data", io.data)), Seq(("io_info", io.info)))
override def modPorts = super.modPorts ++ Seq(Seq(("io", io)))
override def desiredName: String = "DifftestBatch"
override def dpicFuncAssigns: Seq[String] = {
@ -225,34 +221,42 @@ class DPICBatch(template: Seq[DifftestBundle], batchIO: BatchIO, config: Gateway
""".stripMargin
}.mkString("")
def parseInfo(io_info: Data): (String, Int) = {
def parse(gen: BatchIO): (String, Int) = {
val info = new BatchInfo
val infoLen = io_info.getWidth / info.getWidth
val infoDecl =
val infoLen = gen.info.getWidth / info.getWidth
val structDecl =
s"""
| static struct {
| ${info.elements.toSeq.map { case (name, data) => getDPICArgString(name, data, true) }
| typedef struct {
| ${info.elements.toSeq.map { case (name, data) => getDPICArgString(name, data, true, false) }
.mkString(";\n ")};
| } info[$infoLen];
| } BatchInfo;
| typedef struct {
| ${gen.elements.toSeq.map { case (name, data) =>
if (name == "info") s"BatchInfo info[$infoLen]" else getDPICArgString(name, data, true, false)
}.mkString(";\n ")};
| } BatchPack;
| BatchPack* batch = (BatchPack*)io;
| BatchInfo* info = batch->info;
| uint8_t* data = batch->data;
|""".stripMargin
(infoDecl, infoLen)
(structDecl, infoLen)
}
val (infoDecl, infoLen) = parseInfo(io.info)
val (batchDecl, infoLen) = parse(batchIO)
Seq(s"""
| enum DifftestBundleType {
| ${bundleEnum.mkString(",\n ")}
| };
|
| uint64_t offset = 0;
| extern void simv_nstep(uint8_t step);
| uint32_t dut_index = 0;
| $infoDecl
| memcpy(info, io_info, sizeof(info));
| uint8_t* data = (uint8_t*)io_data;
| $batchDecl
| for (int i = 0; i < $infoLen; i++) {
| uint8_t id = info[i].id;
| uint8_t num = info[i].num;
| uint32_t coreid, index, address;
| if (id == BatchFinish) {
|#ifdef CONFIG_DIFFTEST_INTERNAL_STEP
| simv_nstep(num);
|#endif // CONFIG_DIFFTEST_INTERNAL_STEP
| break;
| }
| else if (id == BatchInterval && i != 0) {
@ -288,8 +292,7 @@ private class DummyDPICBatchWrapper(
dpic.clock := clock
dpic.enable := control.enable
if (config.hasDutZone) dpic.dut_zone.get := control.dut_zone.get
if (config.hasInternalStep) dpic.step.get := control.step.get
dpic.io := io
dpic.io := io.asUInt
}
object DPIC {

View File

@ -46,13 +46,14 @@ case class GatewayConfig(
traceLoad: Boolean = false,
hierarchicalWiring: Boolean = false,
exitOnAssertions: Boolean = false,
isFPGA: Boolean = false,
) {
def dutZoneSize: Int = if (hasDutZone) 2 else 1
def dutZoneWidth: Int = log2Ceil(dutZoneSize)
def dutBufLen: Int = if (isBatch) batchSize else 1
def maxStep: Int = if (isBatch) batchSize else 1
def stepWidth: Int = log2Ceil(maxStep + 1)
def batchArgByteLen: (Int, Int) = if (isNonBlock) (3900, 92) else (7800, 192)
def batchArgByteLen: (Int, Int) = if (isNonBlock) (3900, 100) else (7800, 200)
def hasDeferredResult: Boolean = isNonBlock || hasInternalStep
def needTraceInfo: Boolean = hasReplay
def needEndpoint: Boolean =
@ -64,7 +65,12 @@ case class GatewayConfig(
macros += s"CONFIG_DIFFTEST_${style.toUpperCase}"
macros += s"CONFIG_DIFFTEST_ZONESIZE $dutZoneSize"
macros += s"CONFIG_DIFFTEST_BUFLEN $dutBufLen"
if (isBatch) macros ++= Seq("CONFIG_DIFFTEST_BATCH", s"CONFIG_DIFFTEST_BATCH_SIZE ${batchSize}")
if (isBatch)
macros ++= Seq(
"CONFIG_DIFFTEST_BATCH",
s"CONFIG_DIFFTEST_BATCH_SIZE ${batchSize}",
s"CONFIG_DIFFTEST_BATCH_BYTELEN ${batchArgByteLen._1 + batchArgByteLen._2}",
)
if (isSquash) macros ++= Seq("CONFIG_DIFFTEST_SQUASH", s"CONFIG_DIFFTEST_SQUASH_STAMPSIZE 4096") // Stamp Width 12
if (hasReplay) macros ++= Seq("CONFIG_DIFFTEST_REPLAY", s"CONFIG_DIFFTEST_REPLAY_SIZE ${replaySize}")
if (hasDeferredResult) macros += "CONFIG_DIFFTEST_DEFERRED_RESULT"
@ -84,6 +90,7 @@ case class GatewayConfig(
def check(): Unit = {
if (hasReplay) require(isSquash)
if (hasInternalStep) require(isBatch)
if (isBatch) require(!hasDutZone)
// TODO: support dump and load together
require(!(traceDump && traceLoad))
}
@ -127,6 +134,7 @@ object Gateway {
case 'L' => config = config.copy(traceLoad = true)
case 'H' => config = config.copy(hierarchicalWiring = true)
case 'X' => config = config.copy(exitOnAssertions = true)
case 'F' => config = config.copy(isFPGA = true)
case x => println(s"Unknown Gateway Config $x")
}
config.check()
@ -224,18 +232,8 @@ class GatewayEndpoint(instanceWithDelay: Seq[(DifftestBundle, Int)], config: Gat
if (config.isBatch) {
val batch = Batch(squashed, config)
if (config.hasInternalStep) {
step := batch.step
control.step.get := batch.step
} else {
step := RegNext(batch.step, 0.U)
}
step := RegNext(batch.step, 0.U) // expose Batch step to check timeout
control.enable := batch.enable
if (config.hasDutZone) {
zoneControl.get.enable := batch.enable
control.dut_zone.get := zoneControl.get.dut_zone
}
GatewaySink.batch(Batch.getTemplate, control, batch.io, config)
} else {
val squashed_enable = VecInit(squashed.map(_.valid).toSeq).asUInt.orR
@ -281,7 +279,6 @@ object GatewaySink {
class GatewaySinkControl(config: GatewayConfig) extends Bundle {
val enable = Bool()
val dut_zone = Option.when(config.hasDutZone)(UInt(config.dutZoneWidth.W))
val step = Option.when(config.hasInternalStep)(UInt(config.stepWidth.W))
}
object Preprocess {

View File

@ -44,8 +44,11 @@ void difftest_perfcnt_finish(uint64_t cycleCnt) {
diffstate_perfcnt_finish(perf_run_msec);
printf(">>> Other Difftest Func\n");
const char *func_name[DIFFTEST_PERF_NUM] = {
"simv_nstep", "difftest_ram_read", "difftest_ram_write", "flash_read", "sd_set_addr", "sd_read",
"jtag_tick", "put_pixel", "vmem_sync", "pte_helper", "amo_helper",
#ifndef CONFIG_DIFFTEST_INTERNAL_STEP
"simv_nstep",
#endif // CONFIG_DIFFTEST_INTERNAL_STEP
"difftest_ram_read", "difftest_ram_write", "flash_read", "sd_set_addr", "sd_read",
"jtag_tick", "put_pixel", "vmem_sync", "pte_helper", "amo_helper",
};
for (int i = 0; i < DIFFTEST_PERF_NUM; i++) {
difftest_perfcnt_print(func_name[i], difftest_calls[i], difftest_bytes[i], perf_run_msec);

View File

@ -27,7 +27,9 @@ static inline void difftest_perfcnt_print(const char *name, long long calls, lon
void difftest_perfcnt_init();
void difftest_perfcnt_finish(uint64_t cycleCnt);
enum DIFFTEST_PERF {
#ifndef CONFIG_DIFFTEST_INTERNAL_STEP
perf_simv_nstep,
#endif // CONFIG_DIFFTEST_INTERNAL_STEP
perf_difftest_ram_read,
perf_difftest_ram_write,
perf_flash_read,