CIRCT 23.0.0git
Loading...
Searching...
No Matches
TraceEncoder.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// Provides an abstract TraceEncoder class implementing the trace buffer
10// management and communication between the simulation thread and the trace
11// encoder thread.
12//
13//===----------------------------------------------------------------------===//
14
16
18
19#include <iostream>
20
22
23/// Read the simulation time from the model state. The time is stored as an i64
24/// at offset 0 of the model state.
25static inline int64_t readTimeFromState(const ArcState *state) {
26 return *reinterpret_cast<const int64_t *>(state->modelState);
27}
28
30 ArcState *state, unsigned numBuffers,
31 bool debug = false)
32 : numBuffers(numBuffers), modelInfo(modelInfo), debug(debug),
33 activeBuffer(modelInfo->traceInfo->traceBufferCapacity) {
34 assert(numBuffers > 0);
36 timeStep = 0;
37 simTime = 0;
38 isFinished = false;
39 worker = {};
40 // Put the first buffer in place
42 // Prime the buffer return stack with additional buffers
43 for (unsigned i = 1; i < numBuffers; ++i)
45}
46
47// Enqueue a trace buffer for processing by the worker thread.
49 assert(worker.has_value());
50 std::unique_lock<std::mutex> lock(bufferQueueMutex);
51 bufferQueue.emplace(std::move(buffer));
52 lock.unlock();
53 bufferQueueCv.notify_one();
54}
55
56// Retrieve a buffer from the stack of processed buffers.
58 assert(worker.has_value());
59 std::unique_lock<std::mutex> lock(availableBuffersMutex);
60 availableBuffersCv.wait(lock, [this]() { return !availableBuffers.empty(); });
61 auto buffer = std::move(availableBuffers.back());
62 availableBuffers.pop_back();
63 return buffer;
64}
65
67 if (worker.has_value() || !initialize(state))
68 return;
69 // Flush the trace buffer which may contain data from the init function
70 state->traceBufferSize = 0;
71 timeStep = 0;
75 assert(bufferQueue.empty());
76 // Start the worker thread
77 if (debug)
78 std::cout << "[ArcRuntime] Starting trace worker thread." << std::endl;
79 std::thread newWorker([this]() { this->workLoop(); });
80 worker = std::move(newWorker);
81}
82
85 std::optional<TraceBuffer> work = {};
86 while (true) {
87 {
88 // Take buffer
89 std::unique_lock<std::mutex> lock(bufferQueueMutex);
90 bufferQueueCv.wait(
91 lock, [this]() { return isFinished || !bufferQueue.empty(); });
92
93 // Still work to do?
94 if (bufferQueue.empty() && isFinished)
95 break; // Release bufferQueueMutex
96
97 assert(!bufferQueue.empty());
98 work = std::move(bufferQueue.front());
99 bufferQueue.pop();
100 } // Release bufferQueueMutex
101
102 // Process the taken buffer
103 assert(work->size > 0);
104 encode(*work);
105 work->clear();
106
107 {
108 // Return buffer
109 std::lock_guard<std::mutex> lock(availableBuffersMutex);
110 availableBuffers.emplace_back(std::move(*work));
111 } // Release availableBuffersMutex
112 work.reset();
113 availableBuffersCv.notify_one();
114 }
116}
117
118void TraceEncoder::step(const ArcState *state) {
120 ++timeStep;
121 if (!worker)
122 return;
124 simTime = readTimeFromState(state);
125 if (state->traceBufferSize > 0) {
126 // Mark the beginning of the new step in the active trace buffer
127 auto &offsets = activeBuffer.stepMarkers;
128 if (!offsets.empty() && offsets.back().offset == state->traceBufferSize) {
129 // No new data produced since last time: Bump the existing last step.
130 offsets.back().numSteps++;
131 offsets.back().simTime = simTime;
132 } else {
133 // Store the current offset
134 offsets.emplace_back(state->traceBufferSize, simTime);
135 }
136 } else {
137 // Untouched buffer: Adjust the first step value and simulation time.
141 }
142}
143
144void TraceEncoder::finish(const ArcState *state) {
146 if (worker.has_value()) {
147 // Dispatch the final buffer and request the worker to finish.
148 if (state->traceBufferSize > 0)
150 {
151 std::lock_guard<std::mutex> lock(bufferQueueMutex);
152 isFinished = true;
153 } // Release bufferQueueMutex
154 bufferQueueCv.notify_one();
155 // Wait for the worker to finish.
156 worker->join();
157 worker.reset();
158 if (debug)
159 std::cout << "[ArcRuntime] Trace worker thread finished." << std::endl;
160 }
161 finalize(state);
162}
163
164uint64_t *TraceEncoder::dispatch(uint32_t oldBufferSize) {
166 if (oldBufferSize == 0)
167 impl::fatalError("Trace dispatch called on an empty buffer");
168 if (worker.has_value()) {
169 activeBuffer.size = oldBufferSize;
170 enqueueBuffer(std::move(activeBuffer));
174 }
175 return activeBuffer.getData();
176}
177
178} // namespace circt::arc::runtime::impl
assert(baseType &&"element must be base type")
virtual bool initialize(const ArcState *state)=0
Set-up the encoder before starting the worker thread.
std::atomic< bool > isFinished
Flag signaling that no more buffers will be enqueued.
std::queue< TraceBuffer > bufferQueue
virtual void startUpWorker()
Called by the worker thread before entering the encode loop.
uint64_t simTime
Current simulation time in femtoseconds.
void workLoop()
The encoder thread's work loop.
std::mutex bufferQueueMutex
Queue and synchronization primitives for buffers to be processed by the encoder thread.
virtual void finalize(const ArcState *state)
Finish trace encoding. Called by the simulation thread.
void step(const ArcState *state)
Signal the start of a new simulation time step.
std::mutex availableBuffersMutex
Return stack and synchronization primitives for processed buffers.
virtual void windDownWorker()
Called by the worker thread after leaving the encode loop.
virtual void encode(TraceBuffer &work)
Encode the given trace buffer. Called by the worker thread.
void run(ArcState *state)
Begin tracing.
std::condition_variable availableBuffersCv
const unsigned numBuffers
Number of trace buffers in rotation.
std::vector< TraceBuffer > availableBuffers
void enqueueBuffer(TraceBuffer &&buffer)
int64_t timeStep
Current simulation time step.
const ArcRuntimeModelInfo *const modelInfo
Metadata of the traced model.
TraceBuffer activeBuffer
Trace buffer currently in use by the hardware model.
std::condition_variable bufferQueueCv
void finish(const ArcState *state)
Stop tracing.
uint64_t * dispatch(uint32_t oldBufferSize)
Dispatch the currently active trace buffer containing oldBufferSize valid entries to the encoder thre...
std::optional< std::thread > worker
Trace encoder worker thread. If empty, tracing is disabled.
static void fatalError(const char *message)
Raise an irrecoverable error.
Definition Internal.h:23
static int64_t readTimeFromState(const ArcState *state)
Read the simulation time from the model state.
Definition debug.py:1
uint64_t numTraceTaps
Number of trace taps in the array.
Definition TraceTaps.h:36
uint64_t traceBufferCapacity
Required capacity in 8 byte increments of the trace buffer.
Definition TraceTaps.h:43
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
Combined runtime and model state for a hardware model instance.
Definition Common.h:44
uint8_t modelState[]
Definition Common.h:58
uint64_t * traceBuffer
Pointer to the active trace buffer's storage.
Definition Common.h:50
uint32_t traceBufferSize
Number of valid elements in the active trace buffer.
Definition Common.h:52
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.
void assertSentinel() const
Assert that the buffer's sentinel value has not been overwritten.
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.