CIRCT 22.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
24 ArcState *state, unsigned numBuffers,
25 bool debug = false)
26 : numBuffers(numBuffers), modelInfo(modelInfo), debug(debug),
27 activeBuffer(modelInfo->traceInfo->traceBufferCapacity) {
28 assert(numBuffers > 0);
30 timeStep = 0;
31 isFinished = false;
32 worker = {};
33 // Put the first buffer in place
35 // Prime the buffer return stack with additional buffers
36 for (unsigned i = 1; i < numBuffers; ++i)
38}
39
40// Enqueue a trace buffer for processing by the worker thread.
42 assert(worker.has_value());
43 std::unique_lock<std::mutex> lock(bufferQueueMutex);
44 bufferQueue.emplace(std::move(buffer));
45 lock.unlock();
46 bufferQueueCv.notify_one();
47}
48
49// Retrieve a buffer from the stack of processed buffers.
51 assert(worker.has_value());
52 std::unique_lock<std::mutex> lock(availableBuffersMutex);
53 availableBuffersCv.wait(lock, [this]() { return !availableBuffers.empty(); });
54 auto buffer = std::move(availableBuffers.back());
55 availableBuffers.pop_back();
56 return buffer;
57}
58
60 if (worker.has_value() || !initialize(state))
61 return;
62 // Flush the trace buffer which may contain data from the init function
63 state->traceBufferSize = 0;
64 timeStep = 0;
67 assert(bufferQueue.empty());
68 // Start the worker thread
69 if (debug)
70 std::cout << "[ArcRuntime] Starting trace worker thread." << std::endl;
71 std::thread newWorker([this]() { this->workLoop(); });
72 worker = std::move(newWorker);
73}
74
77 std::optional<TraceBuffer> work = {};
78 while (true) {
79 {
80 // Take buffer
81 std::unique_lock<std::mutex> lock(bufferQueueMutex);
82 bufferQueueCv.wait(
83 lock, [this]() { return isFinished || !bufferQueue.empty(); });
84
85 // Still work to do?
86 if (bufferQueue.empty() && isFinished)
87 break; // Release bufferQueueMutex
88
89 assert(!bufferQueue.empty());
90 work = std::move(bufferQueue.front());
91 bufferQueue.pop();
92 } // Release bufferQueueMutex
93
94 // Process the taken buffer
95 assert(work->size > 0);
96 encode(*work);
97 work->clear();
98
99 {
100 // Return buffer
101 std::lock_guard<std::mutex> lock(availableBuffersMutex);
102 availableBuffers.emplace_back(std::move(*work));
103 } // Release availableBuffersMutex
104 work.reset();
105 availableBuffersCv.notify_one();
106 }
108}
109
110void TraceEncoder::step(const ArcState *state) {
112 ++timeStep;
113 if (!worker)
114 return;
116 if (state->traceBufferSize > 0) {
117 // Mark the beginning of the new step in the active trace buffer
118 auto &offsets = activeBuffer.stepMarkers;
119 if (!offsets.empty() && offsets.back().offset == state->traceBufferSize) {
120 // No new data produced since last time: Bump the existing last step.
121 offsets.back().numSteps++;
122 } else {
123 // Store the current offset
124 offsets.emplace_back(state->traceBufferSize);
125 }
126 } else {
127 // Untouched buffer: Adjust the first step value.
130 }
131}
132
133void TraceEncoder::finish(const ArcState *state) {
135 if (worker.has_value()) {
136 // Dispatch the final buffer and request the worker to finish.
137 if (state->traceBufferSize > 0)
139 {
140 std::lock_guard<std::mutex> lock(bufferQueueMutex);
141 isFinished = true;
142 } // Release bufferQueueMutex
143 bufferQueueCv.notify_one();
144 // Wait for the worker to finish.
145 worker->join();
146 worker.reset();
147 if (debug)
148 std::cout << "[ArcRuntime] Trace worker thread finished." << std::endl;
149 }
150 finalize(state);
151}
152
153uint64_t *TraceEncoder::dispatch(uint32_t oldBufferSize) {
155 if (oldBufferSize == 0)
156 impl::fatalError("Trace dispatch called on an empty buffer");
157 if (worker.has_value()) {
158 activeBuffer.size = oldBufferSize;
159 enqueueBuffer(std::move(activeBuffer));
162 }
163 return activeBuffer.getData();
164}
165
166} // 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.
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
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
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.