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 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 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 ./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: test-difftest-fuzzing:
# This test runs on ubuntu-20.04 for two reasons: # 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, # (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 +no-diff +max-cycles=100000
./build/simv +workload=./ready-to-run/microbench.bin +e=0 +diff=./ready-to-run/riscv64-nemu-interpreter-so ./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: | run: |
cd $GITHUB_WORKSPACE/../xs-env cd $GITHUB_WORKSPACE/../xs-env
source ./env.sh source ./env.sh
cd $GITHUB_WORKSPACE/../xs-env/NutShell cd $GITHUB_WORKSPACE/../xs-env/NutShell
source ./env.sh source ./env.sh
make clean 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 +no-diff +max-cycles=100000
./build/simv +workload=./ready-to-run/microbench.bin +e=0 +diff=./ready-to-run/riscv64-nemu-interpreter-so ./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 val info = infoType
} }
class BatchOutput(data: UInt, info: UInt, config: GatewayConfig) extends Bundle { class BatchOutput(dataType: UInt, infoType: UInt, config: GatewayConfig) extends Bundle {
val io = new BatchIO(chiselTypeOf(data), chiselTypeOf(info)) val io = new BatchIO(dataType, infoType)
val enable = Bool() val enable = Bool()
val step = UInt(config.stepWidth.W) 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 BatchInterval = WireInit(0.U.asTypeOf(new BatchInfo))
val BatchFinish = WireInit(0.U.asTypeOf(new BatchInfo))
BatchInterval.id := Batch.getTemplate.length.U BatchInterval.id := Batch.getTemplate.length.U
BatchFinish.id := (Batch.getTemplate.length + 1).U
val step_data = dataCollect_vec.last val step_data = dataCollect_vec.last
val step_info = Cat(infoCollect_vec.last, BatchInterval.asUInt) val step_info = Cat(infoCollect_vec.last, BatchInterval.asUInt)
val step_data_len = dataLenCollect_vec.last 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.data := state_data
out.io.info := state_info | BatchFinish.asUInt << (state_info_len << 3) out.io.info := state_info | BatchFinish.asUInt << (state_info_len << 3)
out.enable := should_tick 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 clock = IO(Input(Clock()))
val enable = IO(Input(Bool())) val enable = IO(Input(Bool()))
val dut_zone = Option.when(config.hasDutZone)(IO(Input(UInt(config.dutZoneWidth.W)))) 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 = { def getDirectionString(data: Data): String = {
if (DataMirror.directionOf(data) == ActualDirection.Input) "input " else "output" 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 { val typeString = data.getWidth match {
case 1 => if (isC) "uint8_t" else "bit" 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 > 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 > 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 > 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) { if (isC) {
val width = data.getWidth 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 { } else {
val directionString = getDirectionString(data) val directionString = getDirectionString(data)
f"$directionString $typeString%8s $argName" f"$directionString $typeString%8s $argName"
@ -63,7 +68,6 @@ abstract class DPICBase(config: GatewayConfig) extends ExtModule with HasExtModu
def modPorts: Seq[Seq[(String, Data)]] = { def modPorts: Seq[Seq[(String, Data)]] = {
var ports = commonPorts var ports = commonPorts
if (config.hasDutZone) ports ++= Seq(("dut_zone", dut_zone.get)) if (config.hasDutZone) ports ++= Seq(("dut_zone", dut_zone.get))
if (config.hasInternalStep) ports ++= Seq(("step", step.get))
ports.map(Seq(_)) ports.map(Seq(_))
} }
@ -74,7 +78,7 @@ abstract class DPICBase(config: GatewayConfig) extends ExtModule with HasExtModu
def dpicFuncProto: String = def dpicFuncProto: String =
s""" s"""
|extern "C" void $dpicFuncName ( |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 |)""".stripMargin
def getPacketDecl(gen: DifftestBundle, prefix: String, config: GatewayConfig): String = { def getPacketDecl(gen: DifftestBundle, prefix: String, config: GatewayConfig): String = {
val dut_zone = if (config.hasDutZone) "dut_zone" else "0" val dut_zone = if (config.hasDutZone) "dut_zone" else "0"
@ -94,20 +98,12 @@ abstract class DPICBase(config: GatewayConfig) extends ExtModule with HasExtModu
|""".stripMargin |""".stripMargin
} }
def internalStep: String = if (config.hasInternalStep)
"""
|extern void simv_nstep(uint8_t step);
|simv_nstep(step);
|""".stripMargin
else ""
def dpicFunc: String = def dpicFunc: String =
s""" s"""
|$dpicFuncProto { |$dpicFuncProto {
| if (!diffstate_buffer) return; | if (!diffstate_buffer) return;
|$perfCnt |$perfCnt
| ${dpicFuncAssigns.mkString("\n ")} | ${dpicFuncAssigns.mkString("\n ")}
| $internalStep
|} |}
|""".stripMargin |""".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) { 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 = { def getDPICBundleUnpack(gen: DifftestBundle): String = {
val unpack = ListBuffer.empty[String] val unpack = ListBuffer.empty[String]
@ -210,7 +206,7 @@ class DPICBatch(template: Seq[DifftestBundle], batchIO: BatchIO, config: Gateway
unpack.toSeq.mkString("\n ") 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 desiredName: String = "DifftestBatch"
override def dpicFuncAssigns: Seq[String] = { override def dpicFuncAssigns: Seq[String] = {
@ -225,34 +221,42 @@ class DPICBatch(template: Seq[DifftestBundle], batchIO: BatchIO, config: Gateway
""".stripMargin """.stripMargin
}.mkString("") }.mkString("")
def parseInfo(io_info: Data): (String, Int) = { def parse(gen: BatchIO): (String, Int) = {
val info = new BatchInfo val info = new BatchInfo
val infoLen = io_info.getWidth / info.getWidth val infoLen = gen.info.getWidth / info.getWidth
val infoDecl = val structDecl =
s""" s"""
| static struct { | typedef struct {
| ${info.elements.toSeq.map { case (name, data) => getDPICArgString(name, data, true) } | ${info.elements.toSeq.map { case (name, data) => getDPICArgString(name, data, true, false) }
.mkString(";\n ")}; .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 |""".stripMargin
(infoDecl, infoLen) (structDecl, infoLen)
} }
val (infoDecl, infoLen) = parseInfo(io.info) val (batchDecl, infoLen) = parse(batchIO)
Seq(s""" Seq(s"""
| enum DifftestBundleType { | enum DifftestBundleType {
| ${bundleEnum.mkString(",\n ")} | ${bundleEnum.mkString(",\n ")}
| }; | };
| | extern void simv_nstep(uint8_t step);
| uint64_t offset = 0;
| uint32_t dut_index = 0; | uint32_t dut_index = 0;
| $infoDecl | $batchDecl
| memcpy(info, io_info, sizeof(info));
| uint8_t* data = (uint8_t*)io_data;
| for (int i = 0; i < $infoLen; i++) { | for (int i = 0; i < $infoLen; i++) {
| uint8_t id = info[i].id; | uint8_t id = info[i].id;
| uint8_t num = info[i].num; | uint8_t num = info[i].num;
| uint32_t coreid, index, address; | uint32_t coreid, index, address;
| if (id == BatchFinish) { | if (id == BatchFinish) {
|#ifdef CONFIG_DIFFTEST_INTERNAL_STEP
| simv_nstep(num);
|#endif // CONFIG_DIFFTEST_INTERNAL_STEP
| break; | break;
| } | }
| else if (id == BatchInterval && i != 0) { | else if (id == BatchInterval && i != 0) {
@ -288,8 +292,7 @@ private class DummyDPICBatchWrapper(
dpic.clock := clock dpic.clock := clock
dpic.enable := control.enable dpic.enable := control.enable
if (config.hasDutZone) dpic.dut_zone.get := control.dut_zone.get if (config.hasDutZone) dpic.dut_zone.get := control.dut_zone.get
if (config.hasInternalStep) dpic.step.get := control.step.get dpic.io := io.asUInt
dpic.io := io
} }
object DPIC { object DPIC {

View File

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

View File

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