From 6ff127eb514fb507401bd29c8650dac61283fe34 Mon Sep 17 00:00:00 2001 From: Hansung Kim Date: Fri, 19 Jan 2024 22:37:29 -0800 Subject: [PATCH] Write faux memory fuzzer --- src/main/resources/csrc/SimMemFuzzer.c | 9 + src/main/resources/vsrc/SimMemFuzzer.v | 103 +++++++++++ .../scala/radiance/memory/Coalescing.scala | 171 ++++++++++++++++++ src/main/scala/radiance/tile/FuzzerTile.scala | 6 +- 4 files changed, 286 insertions(+), 3 deletions(-) create mode 100644 src/main/resources/csrc/SimMemFuzzer.c create mode 100644 src/main/resources/vsrc/SimMemFuzzer.v diff --git a/src/main/resources/csrc/SimMemFuzzer.c b/src/main/resources/csrc/SimMemFuzzer.c new file mode 100644 index 0000000..fc26903 --- /dev/null +++ b/src/main/resources/csrc/SimMemFuzzer.c @@ -0,0 +1,9 @@ +#ifndef NO_VPI +#include +#include +#endif +#include + +void memfuzz_init(void) { + printf("Hello from C!\n"); +} diff --git a/src/main/resources/vsrc/SimMemFuzzer.v b/src/main/resources/vsrc/SimMemFuzzer.v new file mode 100644 index 0000000..d67880c --- /dev/null +++ b/src/main/resources/vsrc/SimMemFuzzer.v @@ -0,0 +1,103 @@ +// FIXME hardcoded +`define MEMTRACE_DATA_WIDTH 64 +`define MAX_NUM_LANES 32 +`define MEMTRACE_LOGSIZE_WIDTH 8 + +import "DPI-C" function void memfuzz_init( +); + +// Make sure to sync the parameters for: +// (1) import "DPI-C" declaration +// (2) C function declaration +// (3) DPI function calls inside initial/always blocks +import "DPI-C" function void memtrace_query +( + input bit trace_read_ready, + input longint trace_read_cycle, + input int trace_read_lane_id, + output bit trace_read_valid, + output longint trace_read_address, + output bit trace_read_is_store, + output byte trace_read_size, + output longint trace_read_data, + output bit trace_read_finished +); + +module SimMemFuzz #(parameter NUM_LANES = 4) ( + input clock, + input reset + + // input trace_read_ready, + // output [NUM_LANES-1:0] trace_read_valid, + // output [`MEMTRACE_DATA_WIDTH*NUM_LANES-1:0] trace_read_address, + // output [NUM_LANES-1:0] trace_read_is_store, + // output [`MEMTRACE_LOGSIZE_WIDTH*NUM_LANES-1:0] trace_read_size, + // output [`MEMTRACE_DATA_WIDTH*NUM_LANES-1:0] trace_read_data, + // output trace_read_finished +); + + initial begin + memfuzz_init(); + end + + // bit __in_valid [NUM_LANES-1:0]; + // longint __in_address [NUM_LANES-1:0]; + // bit __in_is_store [NUM_LANES-1:0]; + // reg [`MEMTRACE_LOGSIZE_WIDTH-1:0] __in_size [NUM_LANES-1:0]; + // longint __in_data [NUM_LANES-1:0]; + // bit __in_finished; + + // genvar g; + // generate + // for (g = 0; g < NUM_LANES; g = g + 1) begin + // assign trace_read_valid[g] = __in_valid[g]; + // assign trace_read_address[`MEMTRACE_DATA_WIDTH*(g+1)-1:`MEMTRACE_DATA_WIDTH*g] = __in_address[g]; + + // assign trace_read_is_store[g] = __in_is_store[g]; + // assign trace_read_size[`MEMTRACE_LOGSIZE_WIDTH*(g+1)-1:`MEMTRACE_LOGSIZE_WIDTH*g] = __in_size[g]; + // assign trace_read_data[`MEMTRACE_DATA_WIDTH*(g+1)-1:`MEMTRACE_DATA_WIDTH*g] = __in_data[g]; + // end + // endgenerate + // assign trace_read_finished = __in_finished; + + // initial begin + // /* $value$plusargs("uartlog=%s", __uartlog); */ + // memtrace_init(FILENAME, HAS_SOURCE); + // end + + // always @(posedge clock) begin + // if (reset) begin + // for (integer tid = 0; tid < NUM_LANES; tid = tid + 1) begin + // __in_valid[tid] = 1'b0; + // __in_address[tid] = `MEMTRACE_DATA_WIDTH'b0; + // + // __in_is_store[tid] = 1'b0; + // __in_size[tid] = `MEMTRACE_LOGSIZE_WIDTH'b0; + // __in_data[tid] = `MEMTRACE_DATA_WIDTH'b0; + // end + // __in_finished = 1'b0; + // end else begin + // // We have to write to __in_ regs only when trace_read_ready, or + // // otherwise we might overwrite lines that were previously valid + // // but the downstream missed by being not ready. + // if (trace_read_ready) begin + // for (integer tid = 0; tid < NUM_LANES; tid = tid + 1) begin + // memtrace_query( + // trace_read_ready, + // trace_read_cycle, + // tid, + + // __in_valid[tid], + // __in_address[tid], + + // __in_is_store[tid], + // __in_size[tid], + // __in_data[tid], + + // __in_finished + // ); + // end + // end + // end + // end +endmodule diff --git a/src/main/scala/radiance/memory/Coalescing.scala b/src/main/scala/radiance/memory/Coalescing.scala index f3b2ca9..765379d 100644 --- a/src/main/scala/radiance/memory/Coalescing.scala +++ b/src/main/scala/radiance/memory/Coalescing.scala @@ -1695,6 +1695,18 @@ class MemTraceDriverImp( } } +class SimMemFuzzer extends BlackBox + with HasBlackBoxResource { + val io = IO(new Bundle { + val clock = Input(Clock()) + val reset = Input(Bool()) + }) + + addResource("/vsrc/SimDefaults.vh") + addResource("/vsrc/SimMemFuzzer.v") + addResource("/csrc/SimMemFuzzer.c") +} + class SimMemTrace(filename: String, numLanes: Int, traceHasSource: Boolean) extends BlackBox( Map( @@ -2041,6 +2053,165 @@ object TLPrintf { } } +class MemFuzzer( + config: CoalescerConfig, +)(implicit p: Parameters) + extends LazyModule { + val laneNodes = Seq.tabulate(config.numLanes) { i => + val clientParam = Seq( + TLMasterParameters.v1( + name = "MemTraceDriver" + i.toString, + sourceId = IdRange(0, config.numOldSrcIds) + // visibility = Seq(AddressSet(0x0000, 0xffffff)) + ) + ) + TLClientNode(Seq(TLMasterPortParameters.v1(clientParam))) + } + + val node = TLIdentityNode() + laneNodes.foreach { l => node := l } + + lazy val module = new MemFuzzerImp(this, config) +} + +class MemFuzzerImp( + outer: MemFuzzer, + config: CoalescerConfig, +) extends LazyModuleImp(outer) { + val io = IO(new Bundle { + val finished = Output(Bool()) + }) + val sim = Module(new SimMemFuzzer) + sim.io.clock := clock + sim.io.reset := reset.asBool + + // Read output from Verilog BlackBox + // Split output of SimMemTrace, which is flattened across all lanes,back to each lane's. + val laneReqs = Wire(Vec(config.numLanes, new TraceLine)) + val addrW = laneReqs(0).address.getWidth + val sizeW = laneReqs(0).size.getWidth + val dataW = laneReqs(0).data.getWidth + laneReqs.zipWithIndex.foreach { case (req, i) => + // req.valid := sim.io.trace_read.valid(i) + // req.source := 0.U // driver trace doesn't contain source id + // req.address := sim.io.trace_read.address(addrW * (i + 1) - 1, addrW * i) + // req.is_store := sim.io.trace_read.is_store(i) + // req.size := sim.io.trace_read.size(sizeW * (i + 1) - 1, sizeW * i) + // req.data := sim.io.trace_read.data(dataW * (i + 1) - 1, dataW * i) + req.valid := 0.U + req.source := 0.U // driver trace doesn't contain source id + req.address := 0.U + req.is_store := 0.U + req.size := 0.U + req.data := 0.U + } + + val sourceGens = Seq.fill(config.numLanes)( + Module( + new SourceGenerator( + log2Ceil(config.numOldSrcIds), + ignoreInUse = false + ) + ) + ) + + // Take requests off of the queue and generate TL requests + (outer.laneNodes zip laneReqs).zipWithIndex.foreach { + case ((node, req), lane) => + val (tlOut, edge) = node.out(0) + + // Core only makes accesses of granularity larger than a word, so we want + // the trace driver to act so as well. + // That means if req.size is smaller than word size, we need to pad data + // with zeros to generate a word-size request, and set mask accordingly. + val offsetInWord = req.address % config.wordSizeInBytes.U + val subword = req.size < log2Ceil(config.wordSizeInBytes).U + + // `mask` is currently unused + // val mask = Wire(UInt(config.wordSizeInBytes.W)) + val wordData = Wire(UInt((config.wordSizeInBytes * 8 * 2).W)) + val sizeInBytes = Wire(UInt((sizeW + 1).W)) + sizeInBytes := (1.U) << req.size + // mask := Mux(subword, (~((~0.U(64.W)) << sizeInBytes)) << offsetInWord, ~0.U) + wordData := Mux(subword, req.data << (offsetInWord * 8.U), req.data) + val wordAlignedAddress = + req.address & ~((1 << log2Ceil(config.wordSizeInBytes)) - 1).U(addrW.W) + val wordAlignedSize = Mux(subword, 2.U, req.size) + + val sourceGen = sourceGens(lane) + sourceGen.io.gen := tlOut.a.fire + sourceGen.io.reclaim.valid := tlOut.d.fire + sourceGen.io.reclaim.bits := tlOut.d.bits.source + sourceGen.io.meta := DontCare + + val (plegal, pbits) = edge.Put( + fromSource = sourceGen.io.id.bits, + toAddress = wordAlignedAddress, + lgSize = wordAlignedSize, // trace line already holds log2(size) + // data should be aligned to beatBytes + data = + (wordData << (8.U * (wordAlignedAddress % edge.manager.beatBytes.U))).asUInt + ) + val (glegal, gbits) = edge.Get( + fromSource = sourceGen.io.id.bits, + toAddress = wordAlignedAddress, + lgSize = wordAlignedSize + ) + val legal = Mux(req.is_store, plegal, glegal) + val bits = Mux(req.is_store, pbits, gbits) + + tlOut.a.valid := req.valid && sourceGen.io.id.valid + // req.ready := tlOut.a.ready && sourceGen.io.id.valid + + when(tlOut.a.fire) { + assert(legal, "illegal TL req gen") + } + tlOut.a.bits := bits + tlOut.b.ready := true.B + tlOut.c.valid := false.B + tlOut.d.ready := true.B + tlOut.e.valid := false.B + + // debug + dontTouch(req) + when(tlOut.a.valid) { + TLPrintf( + "MemFuzzer", + tlOut.a.bits.source, + tlOut.a.bits.address, + tlOut.a.bits.size, + tlOut.a.bits.mask, + req.is_store, + tlOut.a.bits.data, + req.data + ) + } + dontTouch(tlOut.a) + dontTouch(tlOut.d) + } + + // Give some slack time after trace EOF to get some outstanding responses + // back. + // val traceFinished = RegInit(false.B) + // when(sim.io.trace_read.finished) { + // traceFinished := true.B + // } + // io.finished := traceFinished + io.finished := true.B + + // FIXME: + // + // currently the .cc file ouptuts finished=true while it still need to issue one more request + // val noValidReqs = sim.io.trace_read.valid === 0.U + // val allReqReclaimed = !(sourceGens.map(_.io.inflight).reduce(_ || _)) + + // when(traceFinished && allReqReclaimed && noValidReqs) { + // assert( + // false.B, + // "\n\n\nsimulation Successfully finished\n\n\n (this assertion intentional fail upon MemTracer termination)" + // ) + // } +} // Synthesizable unit tests class DummyDriver(config: CoalescerConfig)(implicit p: Parameters) diff --git a/src/main/scala/radiance/tile/FuzzerTile.scala b/src/main/scala/radiance/tile/FuzzerTile.scala index 211ca1b..ef8027a 100644 --- a/src/main/scala/radiance/tile/FuzzerTile.scala +++ b/src/main/scala/radiance/tile/FuzzerTile.scala @@ -62,14 +62,14 @@ class FuzzerTile private ( require(p(CoalescerKey).isDefined, "FuzzerTile requires coalescer key to be defined") val coalParam = p(CoalescerKey).get val coalescer = LazyModule(new CoalescingUnit(coalParam)) - val tracer = LazyModule(new MemTraceDriver(coalParam, "fixme")) + val fuzzer = LazyModule(new MemFuzzer(coalParam)) - coalescer.cpuNode :=* TLWidthWidget(4) :=* tracer.node + coalescer.cpuNode :=* TLWidthWidget(4) :=* fuzzer.node masterNode :=* coalescer.aggregateNode override lazy val module = new FuzzerTileModuleImp(this) } class FuzzerTileModuleImp(outer: FuzzerTile) extends BaseTileModuleImp(outer) { - outer.reportCease(Some(true.B)) + outer.reportCease(Some(outer.fuzzer.module.io.finished)) }