CIRCT 24.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
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.
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
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.