CIRCT 23.0.0git
Loading...
Searching...
No Matches
FSTTraceEncoder.cpp
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// TraceEncoder subclass converting and outputting a stream of raw trace buffers
10// to an FST file.
11//
12//===----------------------------------------------------------------------===//
13
15
16#include <algorithm>
17#include <cassert>
18#include <cctype>
19#include <iostream>
20#include <string>
21#include <string_view>
22
23#include "third_party/libfst/fstapi.h"
24
25using namespace circt::arc::runtime::impl;
26
27namespace {
28
29// Helper for referencing the name of a signal within the model's name array
30struct SignalNameRef {
31 SignalNameRef(uint64_t signalIndex, uint64_t nameOffset)
32 : signalIndex(signalIndex), nameOffset(nameOffset) {}
33
34 uint64_t signalIndex;
35 uint64_t nameOffset;
36
37 std::string_view getStringView(const char *nameBlob) const {
38 return std::string_view(nameBlob + nameOffset);
39 }
40};
41
42static void appendLegalizedName(std::string &buffer,
43 const std::string_view &name) {
44 if (name.empty()) {
45 buffer.append("<EMPTY>");
46 return;
47 }
48 for (auto c : name) {
49 if (c == ' ')
50 buffer.push_back('_');
51 else if (std::isprint(c))
52 buffer.push_back(c);
53 }
54}
55
56} // namespace
57
59
61 ArcState *state,
62 const std::filesystem::path &outFilePath,
63 bool debug)
64 : TraceEncoder(modelInfo, state, numTraceBuffers, debug),
65 outFilePath(outFilePath) {}
66
68 const auto *info = modelInfo->traceInfo;
69 signalTable.reserve(info->numTraceTaps);
70 for (uint64_t i = 0; i < info->numTraceTaps; ++i)
71 signalTable.emplace_back(i, info->traceTaps[i].stateOffset,
72 info->traceTaps[i].typeBits);
73}
74
76 assert(!signalTable.empty());
77 const auto *info = modelInfo->traceInfo;
78 const char *sigNameBlob = info->traceTapNames;
79 auto nameRefVector = std::vector<SignalNameRef>();
80 // Start of the name/alias
81 uint64_t sigOffset = 0;
82 // Start of the next signal's first name
83 uint64_t sigOffsetNext = info->traceTaps[0].nameOffset + 1;
84 uint64_t signalIndex = 0;
85 while (signalIndex < info->numTraceTaps) {
86 nameRefVector.emplace_back(signalIndex, sigOffset);
87 auto nameLen = nameRefVector.back().getStringView(sigNameBlob).length();
88 // Advance to the next name/alias
89 sigOffset += nameLen + 1;
90 if (sigOffset >= sigOffsetNext) {
91 // We've reached the next signal
92 assert(sigOffset == sigOffsetNext);
93 ++signalIndex;
94 // Bump to the new next signal
95 if (signalIndex < info->numTraceTaps)
96 sigOffsetNext = info->traceTaps[signalIndex].nameOffset + 1;
97 }
98 }
99
100 // Sort lexicographically
101 std::stable_sort(
102 nameRefVector.begin(), nameRefVector.end(),
103 [sigNameBlob](const SignalNameRef &a, const SignalNameRef &b) {
104 return a.getStringView(sigNameBlob) < b.getStringView(sigNameBlob);
105 });
106
107 std::vector<std::string> currentScope;
108 for (auto &name : nameRefVector) {
109 auto nameStr = name.getStringView(sigNameBlob);
110 std::vector<std::string> sigScope;
111 size_t charOffset = 0;
112 // Push the signal's scope names onto the stack
113 auto newOffset = std::string::npos;
114 while ((newOffset = nameStr.find('/', charOffset)) != std::string::npos) {
115 std::string scopeName(nameStr.substr(charOffset, newOffset - charOffset));
116 std::string legalizedScope;
117 appendLegalizedName(legalizedScope, scopeName);
118 sigScope.push_back(legalizedScope);
119 charOffset = newOffset + 1;
120 }
121 // Count how many scopes match the current scope
122 unsigned commonScopes = 0;
123 for (size_t i = 0; i < std::min(currentScope.size(), sigScope.size());
124 ++i) {
125 if (sigScope[i] == currentScope[i])
126 ++commonScopes;
127 else
128 break;
129 }
130 // Pop scopes until we have reached the first common scope
131 while (commonScopes < currentScope.size()) {
132 currentScope.pop_back();
133 fstWriterSetUpscope(fstWriter);
134 }
135 // Push the new scopes
136 while (sigScope.size() > currentScope.size()) {
137 fstWriterSetScope(fstWriter, FST_ST_VCD_MODULE,
138 sigScope[currentScope.size()].c_str(), nullptr);
139 currentScope.push_back(sigScope[currentScope.size()]);
140 }
141 // Write the signal declaration
142 std::string sigName;
143 appendLegalizedName(sigName, nameStr.substr(charOffset));
144 uint32_t handle = fstWriterCreateVar(
145 fstWriter, FST_VT_VCD_WIRE, FST_VD_IMPLICIT,
146 signalTable[name.signalIndex].numBits, sigName.c_str(),
147 signalTable[name.signalIndex].handle);
148 assert(handle != 0);
149 signalTable[name.signalIndex].handle = handle;
150 }
151}
152
154 fstWriter = fstWriterCreate(outFilePath.string().c_str(), 1);
155 if (!fstWriter) {
156 std::cerr << "[ArcRuntime] WARNING: Unable to open FST trace file "
157 << outFilePath << " for writing. No trace will be produced."
158 << std::endl;
159 return false;
160 }
161
162 fstWriterSetPackType(fstWriter, FST_WR_PT_LZ4);
163 fstWriterSetTimescaleFromString(fstWriter, "1 fs");
164
165 if (debug)
166 std::cout << "[ArcRuntime] Created FST trace file: " << outFilePath
167 << std::endl;
168
170
171 fstWriterSetScope(fstWriter, FST_ST_VCD_MODULE, modelInfo->modelName,
172 nullptr);
174 fstWriterSetUpscope(fstWriter);
175
176 // Dump the entire initial state
177 fstWriterEmitTimeChange(fstWriter, 0);
178 std::string valStr;
179 for (const auto &sig : signalTable) {
180 assert(sig.stateOffset < modelInfo->numStateBytes);
181 valStr.clear();
182 const uint8_t *data = &state->modelState[sig.stateOffset];
183 for (unsigned n = sig.numBits; n > 0; --n)
184 valStr.push_back((data[(n - 1) / 8] & (1 << ((n - 1) % 8))) ? '1' : '0');
185 fstWriterEmitValueChange(fstWriter, sig.handle, valStr.c_str());
186 }
187
188 return true;
189}
190
192
194 size_t offset = 0;
195 assert(workerStep <= work.firstStep);
196 if (workerStep != work.firstStep) {
197 workerStep = work.firstStep;
198 fstWriterEmitTimeChange(fstWriter, work.firstSimTime);
199 }
200
201 const uint64_t *basePtr = work.getData();
202 auto stepIter = work.stepMarkers.begin();
203
204 std::string valStr;
205 while (offset < work.size) {
206 auto idx = basePtr[offset];
207 ++offset;
208 const void *voidPtr = static_cast<const void *>(basePtr + offset);
209
210 valStr.clear();
211 const uint8_t *data = static_cast<const uint8_t *>(voidPtr);
212 for (unsigned n = signalTable[idx].numBits; n > 0; --n)
213 valStr.push_back((data[(n - 1) / 8] & (1 << ((n - 1) % 8))) ? '1' : '0');
214
215 fstWriterEmitValueChange(fstWriter, signalTable[idx].handle,
216 valStr.c_str());
217 offset += signalTable[idx].getStride();
218 assert(offset <= work.size);
219 // Check if we have reached a new time step marker
220 if (stepIter != work.stepMarkers.end() && stepIter->offset == offset) {
221 workerStep += stepIter->numSteps;
222 fstWriterEmitTimeChange(fstWriter, stepIter->simTime);
223 stepIter++;
224 }
225 }
226 assert(offset == work.size);
227 assert(stepIter == work.stepMarkers.end());
228}
229
231
233 if (fstWriter) {
234 // Finalize the trace file with the final simulation time
236 uint64_t finalSimTime =
237 *reinterpret_cast<const uint64_t *>(state->modelState);
238 fstWriterEmitTimeChange(fstWriter, finalSimTime);
239 fstWriterClose(fstWriter);
240 fstWriter = nullptr;
241 }
242}
243
244} // namespace circt::arc::runtime::impl
assert(baseType &&"element must be base type")
struct::fstWriterContext * fstWriter
FST writer context.
const std::filesystem::path outFilePath
Path to the output file.
void windDownWorker() override
Called by the worker thread after leaving the encode loop.
int64_t workerStep
Current time step of the worker thread.
void encode(TraceBuffer &work) override
Encode the given trace buffer. Called by the worker thread.
bool initialize(const ArcState *state) override
Set-up the encoder before starting the worker thread.
std::vector< FSTSignalTableEntry > signalTable
Table of signals: The index matches their Trace Tap ID.
FSTTraceEncoder(const ArcRuntimeModelInfo *modelInfo, ArcState *state, const std::filesystem::path &outFilePath, bool debug)
void startUpWorker() override
Called by the worker thread before entering the encode loop.
void finalize(const ArcState *state) override
Finish trace encoding. Called by the simulation thread.
void createHierarchy()
Build and dump the signal hierarchy.
void initSignalTable()
Create the table of signals.
Abstract TraceEncoder managing trace buffers and the encoder thread.
int64_t getTimeStep() const
Return the value of the internal step counter.
const ArcRuntimeModelInfo *const modelInfo
Metadata of the traced model.
static void appendLegalizedName(std::string &buffer, const std::string_view &name)
Definition debug.py:1
Static information for a compiled hardware model, generated by the MLIR lowering.
Definition Common.h:70
struct ArcModelTraceInfo * traceInfo
Signal tracing information. NULL iff the model is not trace instrumented.
Definition Common.h:78
uint64_t numStateBytes
Number of bytes required for the model's state.
Definition Common.h:74
const char * modelName
Name of the compiled model.
Definition Common.h:76
Combined runtime and model state for a hardware model instance.
Definition Common.h:44
uint8_t modelState[]
Definition Common.h:58
A heap allocated buffer containing raw trace data and time step markers.
int64_t firstStep
Time step of the buffer's first entry.
std::vector< TraceBufferMarker > stepMarkers
Time step markers.
uint32_t size
Number of valid elements, set on dispatch.
uint64_t * getData() const
Get the pointer to the buffer's storage.
uint64_t firstSimTime
Simulation time in femtoseconds at the buffer's first entry.