diff --git a/t1rocketemu/src/AXI4SlaveAgent.scala b/t1rocketemu/src/AXI4SlaveAgent.scala new file mode 100644 index 000000000..3af3f3727 --- /dev/null +++ b/t1rocketemu/src/AXI4SlaveAgent.scala @@ -0,0 +1,205 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2022-2024 Jiuyang Liu + +package org.chipsalliance.t1.t1rocketemu.dpi + +// TODO: upstream to AMBA as VIP +import chisel3._ +import chisel3.util.circt.dpi.{RawClockedVoidFunctionCall, RawUnclockedNonVoidFunctionCall} +import chisel3.util.{isPow2, log2Ceil} +import org.chipsalliance.amba.axi4.bundle.{ARChannel, ARFlowControl, AWChannel, AWFlowControl, AXI4BundleParameter, AXI4ROIrrevocableVerilog, AXI4RWIrrevocableVerilog, AXI4WOIrrevocableVerilog, BChannel, BFlowControl, RChannel, RFlowControl, WChannel, WFlowControl} + +case class AXI4SlaveAgentParameter(name: String, axiParameter: AXI4BundleParameter, outstanding: Int, readPayloadSize: Int, writePayloadSize: Int) + +class AXI4SlaveAgentInterface(parameter: AXI4SlaveAgentParameter) extends Bundle { + val clock: Clock = Input(Clock()) + val reset: Reset = Input(Reset()) + val channelId: UInt = Input(Const(UInt(64.W))) + // don't issue read DPI + val gateRead: Bool = Input(Bool()) + // don't issue write DPI + val gateWrite: Bool = Input(Bool()) + val channel = Flipped( + org.chipsalliance.amba.axi4.bundle.verilog.irrevocable(parameter.axiParameter) + ) +} + +class WritePayload(length: Int, dataWidth: Int) extends Bundle { + val data = Vec(length, UInt(dataWidth.W)) + // For dataWidth <= 8, align strb to u8 for a simple C-API + val strb = Vec(length, UInt(math.max(8, dataWidth / 8).W)) +} + +class ReadPayload(length: Int,dataWidth: Int) extends Bundle { + val data = Vec(length, UInt(dataWidth.W)) +} + +// consume transaction from DPI, drive RTL signal +class AXI4SlaveAgent(parameter: AXI4SlaveAgentParameter) + extends FixedIORawModule[AXI4SlaveAgentInterface](new AXI4SlaveAgentInterface(parameter)) { + dontTouch(io) + io.channel match { + case channel: AXI4RWIrrevocableVerilog => + new WriteManager(channel) + new ReadManager(channel) + case channel: AXI4ROIrrevocableVerilog => + new ReadManager(channel) + case channel: AXI4WOIrrevocableVerilog => + new WriteManager(channel) + } + + private class WriteManager( + channel: AWChannel with AWFlowControl with WChannel with WFlowControl with BChannel with BFlowControl) { + withClockAndReset(io.clock, io.reset) { + /** There is an aw in the register. */ + val awIssued = RegInit(false.B) + /** There is a w in the register. */ + val last = RegInit(false.B) + + /** memory to store the write payload + * @todo limit the payload size based on the RTL configuration. + */ + val writePayload = RegInit(0.U.asTypeOf(new WritePayload(parameter.writePayloadSize, parameter.axiParameter.dataWidth))) + /** AWID, latch at AW fire, used at B fire. */ + val awid = RegInit(0.U.asTypeOf(chiselTypeOf(channel.AWID))) + val awaddr = RegInit(0.U.asTypeOf(chiselTypeOf(channel.AWADDR))) + val awlen = RegInit(0.U.asTypeOf(chiselTypeOf(channel.AWLEN))) + val awsize = RegInit(0.U.asTypeOf(chiselTypeOf(channel.AWSIZE))) + val awburst = RegInit(0.U.asTypeOf(chiselTypeOf(channel.AWBURST))) + val awlock = RegInit(0.U.asTypeOf(chiselTypeOf(channel.AWLOCK))) + val awcache = RegInit(0.U.asTypeOf(chiselTypeOf(channel.AWCACHE))) + val awprot = RegInit(0.U.asTypeOf(chiselTypeOf(channel.AWPROT))) + val awqos = RegInit(0.U.asTypeOf(chiselTypeOf(channel.AWQOS))) + val awregion = RegInit(0.U.asTypeOf(chiselTypeOf(channel.AWREGION))) + val awuser = RegInit(0.U.asTypeOf(chiselTypeOf(channel.AWUSER))) + + /** index the payload, used to write [[writePayload]] */ + val writeIdx = RegInit(0.U.asTypeOf(UInt(8.W))) + val bFire = channel.BREADY && channel.BVALID + val awFire = channel.AWREADY && channel.AWVALID + val wLastFire = channel.WVALID && channel.WREADY && channel.WLAST + val awExist = channel.AWVALID || awIssued + val wExist = channel.WVALID && channel.WLAST || last + + // AW + channel.AWREADY := !awIssued || (wExist && channel.BREADY) + when(channel.AWREADY && channel.AWVALID) { + awid := channel.AWID + awaddr := channel.AWADDR + awlen := channel.AWLEN + awsize := channel.AWSIZE + awburst := channel.AWBURST + awlock := channel.AWLOCK + awcache := channel.AWCACHE + awprot := channel.AWPROT + awqos := channel.AWQOS + awregion := channel.AWREGION + awuser := channel.AWUSER + } + when(awFire ^ bFire) { + awIssued := awFire + } + + // W + val writePayloadUpdate = WireDefault(writePayload) + channel.WREADY := !last || (awExist && channel.BREADY) + when(channel.WVALID && channel.WREADY) { + writePayload.data(writeIdx) := channel.WDATA + writePayloadUpdate.data(writeIdx) := channel.WDATA + writePayload.strb(writeIdx) := channel.WSTRB.pad(writePayload.strb.getWidth) + writePayloadUpdate.strb(writeIdx) := channel.WSTRB.pad(writePayload.strb.getWidth) + writeIdx := writeIdx + 1.U + when(channel.WLAST) { + writeIdx := 0.U + } + } + when(wLastFire ^ bFire) { + last := wLastFire + } + + // B + channel.BVALID := awExist && wExist + channel.BID := Mux(awIssued, awid, channel.AWID) + channel.BRESP := 0.U(2.W) // OK + channel.BUSER := Mux(awIssued, awuser, channel.AWUSER) + when(channel.BVALID && channel.BREADY) { + RawClockedVoidFunctionCall(s"axi_write_${parameter.name}")( + io.clock, + when.cond && !io.gateWrite, + io.channelId, + // handle AW and W at same beat. + Mux(awIssued, awid.asTypeOf(UInt(64.W)), channel.AWID), + Mux(awIssued, awaddr.asTypeOf(UInt(64.W)), channel.AWADDR), + Mux(awIssued, awlen.asTypeOf(UInt(64.W)), channel.AWLEN), + Mux(awIssued, awsize.asTypeOf(UInt(64.W)), channel.AWSIZE), + Mux(awIssued, awburst.asTypeOf(UInt(64.W)), channel.AWBURST), + Mux(awIssued, awlock.asTypeOf(UInt(64.W)), channel.AWLOCK), + Mux(awIssued, awcache.asTypeOf(UInt(64.W)), channel.AWCACHE), + Mux(awIssued, awprot.asTypeOf(UInt(64.W)), channel.AWPROT), + Mux(awIssued, awqos.asTypeOf(UInt(64.W)), channel.AWQOS), + Mux(awIssued, awregion.asTypeOf(UInt(64.W)), channel.AWREGION), + writePayloadUpdate + ) + } + } + } + + private class ReadManager(channel: ARChannel with ARFlowControl with RChannel with RFlowControl) { + withClockAndReset(io.clock, io.reset) { + class CAMValue extends Bundle { + val arid = UInt(16.W) + val arlen = UInt(8.W) + val readPayload = new ReadPayload(parameter.readPayloadSize, parameter.axiParameter.dataWidth) + val readPayloadIndex = UInt(8.W) + val valid = Bool() + val user: UInt = UInt(channel.ARUSER.getWidth.W) + } + /** CAM to maintain order of read requests. This is maintained as FIFO. */ + val cam: Vec[CAMValue] = RegInit(0.U.asTypeOf(Vec(parameter.outstanding, new CAMValue))) + require(isPow2(parameter.outstanding), "Need to handle pointers") + val arPtr = RegInit(0.U.asTypeOf(UInt(log2Ceil(parameter.outstanding).W))) + val rPtr = RegInit(0.U.asTypeOf(UInt(log2Ceil(parameter.outstanding).W))) + + // AR + channel.ARREADY := !cam(arPtr).valid + when(channel.ARREADY && channel.ARVALID) { + cam(arPtr).arid := channel.ARID + cam(arPtr).arlen := channel.ARLEN + cam(arPtr).user := channel.ARUSER + cam(arPtr).readPayload := RawUnclockedNonVoidFunctionCall(s"axi_read_${parameter.name}", new ReadPayload(parameter.readPayloadSize, parameter.axiParameter.dataWidth))( + when.cond && !io.gateRead, + io.channelId, + channel.ARID.asTypeOf(UInt(64.W)), + channel.ARADDR.asTypeOf(UInt(64.W)), + channel.ARLEN.asTypeOf(UInt(64.W)), + channel.ARSIZE.asTypeOf(UInt(64.W)), + channel.ARBURST.asTypeOf(UInt(64.W)), + channel.ARLOCK.asTypeOf(UInt(64.W)), + channel.ARCACHE.asTypeOf(UInt(64.W)), + channel.ARPROT.asTypeOf(UInt(64.W)), + channel.ARQOS.asTypeOf(UInt(64.W)), + channel.ARREGION.asTypeOf(UInt(64.W)) + ) + cam(arPtr).readPayloadIndex := 0.U + cam(arPtr).valid := true.B + arPtr := arPtr + 1.U + } + + // R + channel.RVALID := cam(rPtr).valid + channel.RID := cam(rPtr).arid + channel.RDATA := cam(rPtr).readPayload.data(cam(rPtr).readPayloadIndex) + channel.RRESP := 0.U // OK + channel.RLAST := (cam(rPtr).arlen === cam(rPtr).readPayloadIndex) && cam(rPtr).valid + channel.RUSER := cam(rPtr).user + when(channel.RREADY && channel.RVALID) { + // increase index + cam(rPtr).readPayloadIndex := cam(rPtr).readPayloadIndex + 1.U + when(channel.RLAST) { + cam(rPtr).valid := false.B + rPtr := rPtr + 1.U + } + } + } + } +} diff --git a/t1rocketemu/src/FPToIEEE.scala b/t1rocketemu/src/FPToIEEE.scala new file mode 100644 index 000000000..82af4541a --- /dev/null +++ b/t1rocketemu/src/FPToIEEE.scala @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2012-2014 The Regents of the University of California +// SPDX-FileCopyrightText: 2016-2017 SiFive, Inc +// SPDX-FileCopyrightText: 2024 Jiuyang Liu +package org.chipsalliance.t1.t1rocketemu + +import chisel3._ +import chisel3.experimental.hierarchy.instantiable +import chisel3.experimental.{SerializableModule, SerializableModuleParameter} +import chisel3.util._ +import org.chipsalliance.rocketv.{FPUHelper, FType} + +object FPToIEEEParameter { + implicit def rwP: upickle.default.ReadWriter[FPToIEEEParameter] = upickle.default.macroRW[FPToIEEEParameter] +} + +class FPToIEEEInput(fLen: Int) extends Bundle { + val typeTag = UInt(2.W) + val data = UInt((fLen + 1).W) +} + +case class FPToIEEEParameter( + useAsyncReset: Boolean, + xLen: Int, + fLen: Int, + minFLen: Int) + extends SerializableModuleParameter +class FPToIEEEInterface(parameter: FPToIEEEParameter) extends Bundle { + val clock = Input(Clock()) + val reset = Input(if (parameter.useAsyncReset) AsyncReset() else Bool()) + val in = Flipped(Valid(new FPToIEEEInput(parameter.fLen))) + val out = Valid(UInt(parameter.fLen.W)) +} + +@instantiable +class FPToIEEE(val parameter: FPToIEEEParameter) + extends FixedIORawModule(new FPToIEEEInterface(parameter)) + with SerializableModule[FPToIEEEParameter] + with ImplicitClock + with ImplicitReset { + override protected def implicitClock: Clock = io.clock + override protected def implicitReset: Reset = io.reset + + val minFLen: Int = parameter.minFLen + val fLen: Int = parameter.fLen + val xLen: Int = parameter.xLen + val helper = new FPUHelper(minFLen, fLen, xLen) + val maxExpWidth = helper.maxExpWidth + val maxSigWidth = helper.maxSigWidth + val floatTypes = helper.floatTypes + val maxType = helper.maxType + val minXLen = helper.minXLen + val nIntTypes = helper.nIntTypes + def ieee(x: UInt, t: FType = maxType) = helper.ieee(x, t) + + val in = io.in.bits + val valid = io.in.valid + + def sextTo(x: UInt, n: Int): UInt = { + require(x.getWidth <= n) + if (x.getWidth == n) x + else Cat(Fill(n - x.getWidth, x(x.getWidth - 1)), x) + } + + val store = VecInit( + floatTypes.map(t => + if (t == FType.H) Fill(maxType.ieeeWidth / minXLen, sextTo(ieee(in.data)(15, 0), minXLen)) + else Fill(maxType.ieeeWidth / t.ieeeWidth, ieee(in.data)(t.ieeeWidth - 1, 0)) + ) + )(in.typeTag) + + io.out.valid := valid + io.out.bits := store +} diff --git a/t1rocketemu/src/TestBench.scala b/t1rocketemu/src/TestBench.scala new file mode 100644 index 000000000..b5c8b02ed --- /dev/null +++ b/t1rocketemu/src/TestBench.scala @@ -0,0 +1,317 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2022 Jiuyang Liu + +package org.chipsalliance.t1.t1rocketemu + +import chisel3._ +import chisel3.experimental.{BaseModule, ExtModule, SerializableModuleGenerator} +import chisel3.experimental.dataview.DataViewable +import chisel3.util.circt.dpi.{RawClockedNonVoidFunctionCall, RawUnclockedNonVoidFunctionCall} +import chisel3.util.{HasExtModuleInline, Mux1H, PopCount, Queue, UIntToOH, Valid} +import org.chipsalliance.amba.axi4.bundle._ +import org.chipsalliance.t1.t1rocketemu.dpi._ +import org.chipsalliance.t1.tile.{T1RocketTile, T1RocketTileParameter} + +class TestBench(generator: SerializableModuleGenerator[T1RocketTile, T1RocketTileParameter]) + extends RawModule + with ImplicitClock + with ImplicitReset { + layer.enable(layers.Verification) + val clockGen = Module(new ExtModule with HasExtModuleInline { + override def desiredName = "ClockGen" + setInline( + s"$desiredName.sv", + s"""module $desiredName(output reg clock, output reg reset); + | export "DPI-C" function dump_wave; + | function dump_wave(input string file); + |`ifdef VCS + | $$fsdbDumpfile(file); + | $$fsdbDumpvars("+all"); + | $$fsdbDumpon; + |`endif + |`ifdef VERILATOR + | $$dumpfile(file); + | $$dumpvars(0); + |`endif + | endfunction; + | + | import "DPI-C" context function void t1rocket_cosim_init(); + | initial begin + | t1rocket_cosim_init(); + | clock = 1'b0; + | reset = 1'b1; + | end + | initial #(100) reset = 1'b0; + | always #10 clock = ~clock; + |endmodule + |""".stripMargin + ) + val clock = IO(Output(Bool())) + val reset = IO(Output(Bool())) + }) + def clock = clockGen.clock.asClock + def reset = clockGen.reset + override def implicitClock = clockGen.clock.asClock + override def implicitReset = clockGen.reset + val dut: T1RocketTile with BaseModule = Module(generator.module()) + dut.io.clock := clock + dut.io.reset := reset + + // control simulation + val simulationTime: UInt = RegInit(0.U(64.W)) + simulationTime := simulationTime + 1.U + + // this initial way cannot happen before reset + val initFlag: Bool = RegInit(false.B) + when(!initFlag) { + initFlag := true.B + printf(cf"""{"event":"SimulationStart","cycle":${simulationTime}}\n""") + } + val watchdog: UInt = RawUnclockedNonVoidFunctionCall("cosim_watchdog", UInt(8.W))(simulationTime(9, 0) === 0.U) + when(watchdog =/= 0.U) { + stop(cf"""{"event":"SimulationStop","reason": ${watchdog},"cycle":${simulationTime}}\n""") + } + + // get resetVector from simulator + dut.io.resetVector := RawUnclockedNonVoidFunctionCall("get_resetvector", Const(UInt(64.W)))(simulationTime === 0.U) + + dut.io.hartid := 0.U + dut.io.debug := 0.U + dut.io.mtip := 0.U + dut.io.msip := 0.U + dut.io.meip := 0.U + dut.io.buserror := 0.U + + // memory driver + Seq( + dut.io.highBandwidthAXI, // index 0 + dut.io.highOutstandingAXI // index 1 + ).map(_.viewAs[AXI4RWIrrevocableVerilog]) + .lazyZip( + Seq("highBandwidthAXI", "highOutstandingAXI") + ) + .zipWithIndex + .foreach { + case ((bundle: AXI4RWIrrevocableVerilog, channelName: String), index: Int) => + val agent = Module( + new AXI4SlaveAgent( + AXI4SlaveAgentParameter( + name = channelName, + axiParameter = bundle.parameter, + outstanding = 4, + readPayloadSize = 1, + writePayloadSize = 1 + ) + ) + ).suggestName(s"axi4_channel${index}_${channelName}") + agent.io.channel match { + case io: AXI4RWIrrevocableVerilog => io <> bundle + } + agent.io.clock := clock + agent.io.reset := reset + agent.io.channelId := index.U + agent.io.gateRead := false.B + agent.io.gateWrite := false.B + } + + val instFetchAXI = dut.io.instructionFetchAXI.viewAs[AXI4ROIrrevocableVerilog] + val instFetchAgent = Module( + new AXI4SlaveAgent( + AXI4SlaveAgentParameter( + name = "instructionFetchAXI", + axiParameter = instFetchAXI.parameter, + outstanding = 4, + readPayloadSize = 1, + writePayloadSize = 1 + ) + ).suggestName("axi4_channel2_instructionFetchAXI") + ) + instFetchAgent.io.channel match { + case io: AXI4ROIrrevocableVerilog => io <> instFetchAXI + } + instFetchAgent.io.clock := clock + instFetchAgent.io.reset := reset + instFetchAgent.io.channelId := 0.U + instFetchAgent.io.gateRead := false.B + instFetchAgent.io.gateWrite := false.B + + val loadStoreAXI = dut.io.loadStoreAXI.viewAs[AXI4RWIrrevocableVerilog] + val loadStoreAgent = Module( + new AXI4SlaveAgent( + AXI4SlaveAgentParameter( + name = "loadStoreAXI", + axiParameter = loadStoreAXI.parameter, + outstanding = 4, + // TODO: add payloadSize config to parameter + readPayloadSize = 8, // todo: align with parameter in the future + writePayloadSize = 8 + ) + ).suggestName("axi4_channel3_loadStoreAXI") + ) + loadStoreAgent.io.channel match { + case io: AXI4RWIrrevocableVerilog => io <> loadStoreAXI + } + loadStoreAgent.io.clock := clock + loadStoreAgent.io.reset := reset + loadStoreAgent.io.channelId := 3.U + loadStoreAgent.io.gateRead := false.B + loadStoreAgent.io.gateWrite := false.B + + // probes + val t1RocketProbe = probe.read(dut.io.t1RocketProbe) + val rocketProbe = t1RocketProbe.rocketProbe.suggestName(s"rocketProbe") + val t1Probe = t1RocketProbe.t1Probe.suggestName(s"t1Probe") + val lsuProbe = t1Probe.lsuProbe.suggestName(s"t1LSUProbe") + val laneProbes = t1Probe.laneProbes.zipWithIndex.map { + case (p, idx) => + val wire = WireDefault(p).suggestName(s"lane${idx}Probe") + wire + } + val laneVrfProbes = t1Probe.laneProbes.map(_.vrfProbe).zipWithIndex.map { + case (p, idx) => + val wire = WireDefault(p).suggestName(s"lane${idx}VrfProbe") + wire + } + val storeUnitProbe = t1Probe.lsuProbe.storeUnitProbe.suggestName("storeUnitProbe") + val otherUnitProbe = t1Probe.lsuProbe.otherUnitProbe.suggestName("otherUnitProbe") + + // output the probes + // rocket reg write + when(rocketProbe.rfWen && !rocketProbe.isVector && rocketProbe.rfWaddr =/= 0.U && !(rocketProbe.waitWen && rocketProbe.waitWaddr =/= 0.U))( + printf( + cf"""{"event":"RegWrite","idx":${rocketProbe.rfWaddr},"data":"${rocketProbe.rfWdata}%x","cycle":${simulationTime}}\n""" + ) + ) + + when(rocketProbe.waitWen && !rocketProbe.isVector && rocketProbe.waitWaddr =/= 0.U)( + printf( + cf"""{"event":"RegWriteWait","idx":${rocketProbe.waitWaddr},"cycle":${simulationTime}}\n""" + ) + ) + + // [[option]] rocket fpu reg write + generator.parameter.fpuParameter.zip(t1RocketProbe.fpuProbe).zip(rocketProbe.fpuScoreboard).map { + case((fpuParameter, fpu), fpuScoreboard) => { + val fpToIEEE = Module(new FPToIEEE(FPToIEEEParameter( + fpuParameter.useAsyncReset, + fpuParameter.xLen, + fpuParameter.fLen, + fpuParameter.minFLen + ))) + val isVectorForLLWrite = RegNext(rocketProbe.isVector, false.B) + + fpToIEEE.io.clock := clock + fpToIEEE.io.reset := reset + fpToIEEE.io.in.valid := fpu.pipeWrite.rfWen || (fpu.loadOrVectorWrite.rfWen && !isVectorForLLWrite) + fpToIEEE.io.in.bits.data := Mux(fpu.pipeWrite.rfWen, fpu.pipeWrite.rfWdata, fpu.loadOrVectorWrite.rfWdata) + fpToIEEE.io.in.bits.typeTag := Mux(fpu.pipeWrite.rfWen, fpu.pipeWrite.rfWtypeTag, fpu.loadOrVectorWrite.rfWtypeTag) + + val rfWen = fpToIEEE.io.out.valid + val rfWaddr = Mux(fpu.pipeWrite.rfWen, fpu.pipeWrite.rfWaddr, fpu.loadOrVectorWrite.rfWaddr) + val rfWdata = fpToIEEE.io.out.bits + when(rfWen) { + printf( + cf"""{"event":"FregWrite","idx":$rfWaddr,"data":"$rfWdata%x","cycle":$simulationTime}\n""" + ) + } + + when(fpuScoreboard.fpuSetScoreBoard && !rfWen) { + printf( + cf"""{"event":"FregWriteWait","idx":${fpuScoreboard.scoreBoardSetAddress},"cycle":${simulationTime}}\n""" + ) + } + when(fpuScoreboard.memSetScoreBoard && !rfWen) { + printf( + cf"""{"event":"FregWriteWait","idx":${fpuScoreboard.scoreBoardSetAddress},"cycle":${simulationTime}}\n""" + ) + } + } + } + + // t1 vrf write + laneVrfProbes.zipWithIndex.foreach { + case (lane, i) => + when(lane.valid)( + printf( + cf"""{"event":"VrfWrite","issue_idx":${lane.requestInstruction},"vd":${lane.requestVd},"offset":${lane.requestOffset},"mask":"${lane.requestMask}%x","data":"${lane.requestData}%x","lane":$i,"cycle":${simulationTime}}\n""" + ) + ) + } + + // t1 memory write from store unit + when(storeUnitProbe.valid)( + printf( + cf"""{"event":"MemoryWrite","lsu_idx":${storeUnitProbe.index},"mask":"${storeUnitProbe.mask}%x","data":"${storeUnitProbe.data}%x","address":"${storeUnitProbe.address}%x","cycle":${simulationTime}}\n""" + ) + ) + + // t1 memory write from other unit + when(otherUnitProbe.valid)( + printf( + cf"""{"event":"MemoryWrite","lsu_idx":${otherUnitProbe.index},"mask":"${otherUnitProbe.mask}%x","data":"${otherUnitProbe.data}%x","address":"${otherUnitProbe.address}%x","cycle":${simulationTime}}\n""" + ) + ) + + // t1 issue + when(t1Probe.issue.valid)( + printf(cf"""{"event":"Issue","idx":${t1Probe.issue.bits},"cycle":${simulationTime}}\n""") + ) + + // t1 retire + when(t1Probe.retire.valid)( + printf( + cf"""{"event":"CheckRd","data":"${t1Probe.retire.bits}%x","issue_idx":${t1Probe.responseCounter},"cycle":${simulationTime}}\n""" + ) + ) + + // t1 lsu enq + when(t1Probe.lsuProbe.reqEnq.orR)( + printf(cf"""{"event":"LsuEnq","enq":${t1Probe.lsuProbe.reqEnq},"cycle":${simulationTime}}\n""") + ) + + // t1 vrf scoreboard + val vrfWriteScoreboard: Seq[Valid[UInt]] = Seq.tabulate(2 * generator.parameter.t1Parameter.chainingSize) { _ => + RegInit(0.U.asTypeOf(Valid(UInt(16.W)))) + } + vrfWriteScoreboard.foreach(scoreboard => dontTouch(scoreboard)) + val instructionValid = + (laneProbes.map(laneProbe => laneProbe.instructionValid ## laneProbe.instructionValid) :+ + lsuProbe.lsuInstructionValid :+ t1Probe.instructionValid).reduce(_ | _) + val scoreboardEnq = + Mux(t1Probe.instructionIssue, UIntToOH(t1Probe.issueTag), 0.U((2 * generator.parameter.t1Parameter.chainingSize).W)) + vrfWriteScoreboard.zipWithIndex.foreach { + case (scoreboard, tag) => + val writeEnq: UInt = VecInit( + // vrf write from lane + laneProbes.flatMap(laneProbe => + laneProbe.slots.map(slot => slot.writeTag === tag.U && slot.writeQueueEnq && slot.writeMask.orR) + ) ++ laneProbes.flatMap(laneProbe => + laneProbe.crossWriteProbe.map(cp => cp.bits.writeTag === tag.U && cp.valid && cp.bits.writeMask.orR) + ) ++ + // vrf write from lsu + lsuProbe.slots.map(slot => slot.dataInstruction === tag.U && slot.writeValid && slot.dataMask.orR) ++ + // vrf write from Sequencer + Some(t1Probe.writeQueueEnq.bits === tag.U && t1Probe.writeQueueEnq.valid && t1Probe.writeQueueEnqMask.orR) + ).asUInt + // always equal to array index + scoreboard.bits := scoreboard.bits + PopCount(writeEnq) + when(scoreboard.valid && !instructionValid(tag)) { + printf( + cf"""{"event":"VrfScoreboard","count":${scoreboard.bits},"issue_idx":${tag},"cycle":${simulationTime}}\n""" + ) + scoreboard.valid := false.B + } + when(scoreboardEnq(tag)) { + scoreboard.valid := true.B + assert(!scoreboard.valid) + scoreboard.bits := 0.U + } + } + + // t1 quit + val quitFlag: Bool = RegInit(false.B) + quitFlag := RawClockedNonVoidFunctionCall("cosim_quit", Bool())(clock, !quitFlag) + when(quitFlag && t1Probe.idle && rocketProbe.idle) { + stop(cf"""{"event":"SimulationEnd", "cycle":${simulationTime}}\n""") + } +}