From af29acdcda0675e542737e4148516bc57ec52d18 Mon Sep 17 00:00:00 2001 From: Hansung Kim Date: Sun, 9 Apr 2023 14:53:02 -0700 Subject: [PATCH] Placeholder for MemTraceLogger C++ code --- src/main/resources/csrc/SimMemTraceLogger.cc | 127 +++++++++++++++++++ src/main/scala/tilelink/Coalescing.scala | 52 ++++++-- 2 files changed, 170 insertions(+), 9 deletions(-) create mode 100644 src/main/resources/csrc/SimMemTraceLogger.cc diff --git a/src/main/resources/csrc/SimMemTraceLogger.cc b/src/main/resources/csrc/SimMemTraceLogger.cc new file mode 100644 index 0000000..736d0ba --- /dev/null +++ b/src/main/resources/csrc/SimMemTraceLogger.cc @@ -0,0 +1,127 @@ +#ifndef NO_VPI +#include +#include +#endif +#include +#include +#include +#include +#include +#include "SimMemTraceLogger.h" + +// Global singleton instance +static std::unique_ptr logger; + +MemTraceLogger::MemTraceLogger(const std::string &filename) { + char cwd[4096]; + if (getcwd(cwd, sizeof(cwd))) { + printf("MemTraceLogger: current working dir: %s\n", cwd); + } + + infile.open(filename); + if (infile.fail()) { + fprintf(stderr, "failed to open file %s\n", filename.c_str()); + } +} + +MemTraceLogger::~MemTraceLogger() { + infile.close(); + printf("MemTraceLogger destroyed\n"); +} + +#if 0 +// Parse trace file in its entirety and store it into internal structure. +// TODO: might block for a long time when the trace gets big, check if need to +// be broken down +void MemTraceReader::parse() { + MemTraceLine line; + + printf("MemTraceReader: started parsing\n"); + + while (infile >> line.cycle >> line.loadstore >> line.core_id >> + line.lane_id >> std::hex >> line.address >> line.data >> std::dec >> + line.data_size) { + line.valid = true; + trace.push_back(line); + } + read_pos = trace.cbegin(); + + printf("MemTraceReader: finished parsing\n"); +} + +// Try to read a memory request that might have happened at a given cycle, on a +// given SIMD lane (= "thread"). In case no request happened at that point, +// return an empty line with .valid = false. +MemTraceLine MemTraceReader::read_trace_at(const long cycle, + const int lane_id) { + MemTraceLine line; + line.valid = false; + + // printf("tick(): cycle=%ld\n", cycle); + + if (finished()) { + return line; + } + + line = *read_pos; + // It should always be guaranteed that we consumed all of the past lines, and + // the next line is in the future. + if (line.cycle < cycle) { + // fprintf(stderr, "line.cycle=%ld, cycle=%ld\n", line.cycle, cycle); + assert(false && "some trace lines are left unread in the past"); + } + + if (line.lane_id != lane_id) { + line.valid = false; + } + if (line.cycle > cycle) { + // We haven't reached the cycle mark specified in this line yet, so we don't + // read it right now. + return MemTraceLine{}; + } else if (line.cycle == cycle && line.lane_id == lane_id) { + printf("fire! cycle=%ld, valid=%d, %s addr=%x \n", cycle, line.valid, + line.loadstore, line.address); + + // FIXME! Currently lane_id is assumed to be in round-robin order, e.g. + // 0->1->2->3->0->..., both in the trace file and the order the caller calls + // this function. If this is not true, we cannot simply monotonically + // increment read_pos. + + // Only advance pointer when cycle and threa_id both match + // now increaseing sequence is fine (0, 1, 3), but unordered is not fine (0, 3, 1) + ++read_pos; + } + + return line; +} +#endif + +extern "C" void memtracelogger_init(const char *filename) { +#ifndef NO_VPI + s_vpi_vlog_info info; + if (!vpi_get_vlog_info(&info)) { + fprintf(stderr, "fatal: failed to get plusargs from VCS\n"); + exit(1); + } + const char* TRACEFILENAME_PLUSARG = "+memtracefile="; + for (int i = 0; i < info.argc; i++) { + char* input_arg = info.argv[i]; + if (strncmp(input_arg, TRACEFILENAME_PLUSARG, + strlen(TRACEFILENAME_PLUSARG)) == 0) { + filename = input_arg + strlen(TRACEFILENAME_PLUSARG); + break; + } + } +#endif + + printf("memtrace_init: filename=[%s]\n", filename); + + logger = std::make_unique(filename); +} + +// TODO: accept core_id as well +extern "C" void memtracelogger_log(unsigned char *trace_log_ready) { + // printf("memtrace_query(cycle=%ld, tid=%d)\n", trace_read_cycle, + // trace_read_lane_id); + *trace_log_ready = 1; +} diff --git a/src/main/scala/tilelink/Coalescing.scala b/src/main/scala/tilelink/Coalescing.scala index 80bf9bc..8d318a0 100644 --- a/src/main/scala/tilelink/Coalescing.scala +++ b/src/main/scala/tilelink/Coalescing.scala @@ -584,7 +584,7 @@ class CoalShiftQueue[T <: Data]( io.count := PopCount(io.mask) } -class MemTraceDriver(numLanes: Int = 4, traceFile: String = "vecadd.core1.thread4.trace")(implicit +class MemTraceDriver(numLanes: Int = 4, filename: String = "vecadd.core1.thread4.trace")(implicit p: Parameters ) extends LazyModule { @@ -605,7 +605,7 @@ class MemTraceDriver(numLanes: Int = 4, traceFile: String = "vecadd.core1.thread val node = TLIdentityNode() laneNodes.foreach { l => node := l } - lazy val module = new MemTraceDriverImp(this, numLanes, traceFile) + lazy val module = new MemTraceDriverImp(this, numLanes, filename) } class TraceReq extends Bundle { @@ -727,7 +727,7 @@ class SimMemTrace(filename: String, numLanes: Int) addResource("/csrc/SimMemTrace.h") } -class MemTraceLogger(numLanes: Int = 4)(implicit p: Parameters) extends LazyModule { +class MemTraceLogger(numLanes: Int = 4, filename: String = "vecadd.core1.thread4.trace")(implicit p: Parameters) extends LazyModule { val node = TLIdentityNode() // val beatBytes = 8 // FIXME: hardcoded @@ -747,14 +747,47 @@ class MemTraceLogger(numLanes: Int = 4)(implicit p: Parameters) extends LazyModu lazy val module = new Impl class Impl extends LazyModuleImp(this) { - (node.in zip node.out).foreach { - case ((tlIn, _), (tlOut, _)) => - tlOut.a <> tlIn.a - tlIn.d <> tlOut.d + val sim = Module(new SimMemTraceLogger(filename, numLanes)) + sim.io.clock := clock + sim.io.reset := reset.asBool + + (node.in zip node.out).foreach { case ((tlIn, _), (tlOut, _)) => + tlOut.a <> tlIn.a + tlIn.d <> tlOut.d } + + // io.finished := sim.io.trace_read.finished } } +class SimMemTraceLogger(filename: String, numLanes: Int) + extends BlackBox( + Map("FILENAME" -> filename, "NUM_LANES" -> numLanes) + ) + with HasBlackBoxResource { + val io = IO(new Bundle { + val clock = Input(Clock()) + val reset = Input(Bool()) + + // val trace_read = new Bundle { + // val ready = Input(Bool()) + // val valid = Output(UInt(numLanes.W)) + // // Chisel can't interface with Verilog 2D port, so flatten all lanes into + // // single wide 1D array. + // // TODO: assumes 64-bit address. + // val address = Output(UInt((64 * numLanes).W)) + // val is_store = Output(UInt(numLanes.W)) + // val store_mask = Output(UInt((8 * numLanes).W)) + // val data = Output(UInt((64 * numLanes).W)) + // val finished = Output(Bool()) + // } + }) + + addResource("/vsrc/SimMemTraceLogger.v") + addResource("/csrc/SimMemTraceLogger.cc") + addResource("/csrc/SimMemTraceLogger.h") +} + // synthesizable unit tests // tracedriver --> coalescer --> tracelogger --> tlram @@ -781,7 +814,8 @@ class TLRAMCoalescerLogger(implicit p: Parameters) extends LazyModule { } } -class TLRAMCoalescerLoggerTest(timeout: Int = 500000)(implicit p: Parameters) extends UnitTest(timeout) { +class TLRAMCoalescerLoggerTest(timeout: Int = 500000)(implicit p: Parameters) + extends UnitTest(timeout) { val dut = Module(LazyModule(new TLRAMCoalescerLogger).module) dut.io.start := io.start io.finished := dut.io.finished @@ -793,7 +827,7 @@ class TLRAMCoalescer(implicit p: Parameters) extends LazyModule { val numLanes = 4 val coal = LazyModule(new CoalescingUnit(numLanes)) val driver = LazyModule(new MemTraceDriver(numLanes)) - val rams = Seq.fill(numLanes + 1) ( // +1 for coalesced edge + val rams = Seq.fill(numLanes + 1)( // +1 for coalesced edge LazyModule( // FIXME: properly propagate beatBytes? new TLRAM(address = AddressSet(0x0000, 0xffffff), beatBytes = 8)