CIRCT  19.0.0git
State.cpp
Go to the documentation of this file.
1 //===- State.cpp - LLHD simulator state -------------------------*- C++ -*-===//
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 // This file implements the constructs used to keep track of the simulation
10 // state in the LLHD simulator.
11 //
12 //===----------------------------------------------------------------------===//
13 
15 
16 #include "llvm/Support/Format.h"
17 #include "llvm/Support/raw_ostream.h"
18 
19 #include <string>
20 
21 using namespace llvm;
22 using namespace circt::llhd::sim;
23 
24 //===----------------------------------------------------------------------===//
25 // Time
26 //===----------------------------------------------------------------------===//
27 
28 std::string Time::toString() const {
29  return std::to_string(time) + "ps " + std::to_string(delta) + "d " +
30  std::to_string(eps) + "e";
31 }
32 
33 //===----------------------------------------------------------------------===//
34 // Signal
35 //===----------------------------------------------------------------------===//
36 
37 std::string Signal::toHexString() const {
38  std::string ret;
39  raw_string_ostream ss(ret);
40  ss << "0x";
41  for (int i = size - 1; i >= 0; --i) {
42  ss << format_hex_no_prefix(static_cast<int>(value[i]), 2);
43  }
44  return ret;
45 }
46 
47 std::string Signal::toHexString(unsigned elemIndex) const {
48  assert(elements.size() > 0 && "the signal type has to be tuple or array!");
49  auto elemSize = elements[elemIndex].second;
50  auto *ptr = value + elements[elemIndex].first;
51  std::string ret;
52  raw_string_ostream ss(ret);
53  ss << "0x";
54  for (int i = elemSize - 1; i >= 0; --i) {
55  ss << format_hex_no_prefix(static_cast<int>(ptr[i]), 2);
56  }
57  return ret;
58 }
59 
60 Signal::~Signal() {
61  std::free(value);
62  value = nullptr;
63 }
64 
65 //===----------------------------------------------------------------------===//
66 // Slot
67 //===----------------------------------------------------------------------===//
68 
69 bool Slot::operator<(const Slot &rhs) const { return time < rhs.time; }
70 
71 bool Slot::operator>(const Slot &rhs) const { return rhs.time < time; }
72 
73 void Slot::insertChange(int index, int bitOffset, uint8_t *bytes,
74  unsigned width) {
75  // Get the amount of 64 bit words required to store the value in an APInt.
76  auto size = llvm::divideCeil(width, 8);
77 
78  APInt buffer(width, 0);
79  llvm::LoadIntFromMemory(buffer, bytes, size);
80  auto offsetBufferPair = std::make_pair(bitOffset, buffer);
81 
82  if (changesSize >= buffers.size()) {
83  // Create a new change buffer if we don't have any unused one available for
84  // reuse.
85  buffers.push_back(offsetBufferPair);
86  } else {
87  // Reuse the first available buffer.
88  buffers[changesSize] = offsetBufferPair;
89  }
90 
91  // Map the signal index to the change buffer so we can retrieve
92  // it after sorting.
93  changes.push_back(std::make_pair(index, changesSize));
94  ++changesSize;
95 }
96 
97 void Slot::insertChange(unsigned inst) { scheduled.push_back(inst); }
98 
99 //===----------------------------------------------------------------------===//
100 // UpdateQueue
101 //===----------------------------------------------------------------------===//
102 void UpdateQueue::insertOrUpdate(Time time, int index, int bitOffset,
103  uint8_t *bytes, unsigned width) {
104  auto &slot = getOrCreateSlot(time);
105  slot.insertChange(index, bitOffset, bytes, width);
106 }
107 
108 void UpdateQueue::insertOrUpdate(Time time, unsigned inst) {
109  auto &slot = getOrCreateSlot(time);
110  slot.insertChange(inst);
111 }
112 
113 Slot &UpdateQueue::getOrCreateSlot(Time time) {
114  auto &top = begin()[topSlot];
115 
116  // Directly add to top slot.
117  if (!top.unused && time == top.time) {
118  return top;
119  }
120 
121  // We need to search through the queue for an existing slot only if we're
122  // spawning an event later than the top slot. Adding to an existing slot
123  // scheduled earlier than the top slot should never happens, as then it should
124  // be the top.
125  if (events > 0 && top.time < time) {
126  for (size_t i = 0, e = size(); i < e; ++i) {
127  if (time == begin()[i].time) {
128  return begin()[i];
129  }
130  }
131  }
132 
133  // Spawn new event using an existing slot.
134  if (!unused.empty()) {
135  auto firstUnused = unused.pop_back_val();
136  auto &newSlot = begin()[firstUnused];
137  newSlot.unused = false;
138  newSlot.time = time;
139 
140  // Update the top of the queue either if it is currently unused or the new
141  // timestamp is earlier than it.
142  if (top.unused || time < top.time)
143  topSlot = firstUnused;
144 
145  ++events;
146  return newSlot;
147  }
148 
149  // We do not have pre-allocated slots available, generate a new one.
150  push_back(Slot(time));
151 
152  // Update the top of the queue either if it is currently unused or the new
153  // timestamp is earlier than it.
154  if (top.unused || time < top.time)
155  topSlot = size() - 1;
156 
157  ++events;
158  return back();
159 }
160 
161 const Slot &UpdateQueue::top() {
162  assert(topSlot < size() && "top is pointing out of bounds!");
163 
164  // Sort the changes of the top slot such that all changes to the same signal
165  // are in succession.
166  auto &top = begin()[topSlot];
167  llvm::sort(top.changes.begin(), top.changes.begin() + top.changesSize);
168  return top;
169 }
170 
171 void UpdateQueue::pop() {
172  // Reset internal structures and decrease the event counter.
173  auto &curr = begin()[topSlot];
174  curr.unused = true;
175  curr.changesSize = 0;
176  curr.scheduled.clear();
177  curr.changes.clear();
178  curr.time = Time();
179  --events;
180 
181  // Add to unused slots list for easy retrieval.
182  unused.push_back(topSlot);
183 
184  // Update the current top of the queue.
185  topSlot = std::distance(
186  begin(),
187  std::min_element(begin(), end(), [](const auto &a, const auto &b) {
188  // a is "smaller" than b if either a's timestamp is earlier than b's, or
189  // b is unused (i.e. b has no actual meaning).
190  return !a.unused && (a < b || b.unused);
191  }));
192 }
193 
194 //===----------------------------------------------------------------------===//
195 // Instance
196 //===----------------------------------------------------------------------===//
197 
198 Instance::~Instance() {
199  std::free(procState);
200  procState = nullptr;
201  std::free(entityState);
202  entityState = nullptr;
203 }
204 
205 //===----------------------------------------------------------------------===//
206 // State
207 //===----------------------------------------------------------------------===//
208 
209 State::~State() {
210  for (auto &inst : instances) {
211  if (inst.procState) {
212  std::free(inst.procState->senses);
213  }
214  }
215 }
216 
217 Slot State::popQueue() {
218  assert(!queue.empty() && "the event queue is empty");
219  Slot pop = queue.top();
220  queue.pop();
221  return pop;
222 }
223 
224 void State::pushQueue(Time t, unsigned inst) {
225  Time newTime = time + t;
226  queue.insertOrUpdate(newTime, inst);
227  instances[inst].expectedWakeup = newTime;
228 }
229 
230 llvm::SmallVectorTemplateCommon<Instance>::iterator
231 State::getInstanceIterator(std::string instName) {
232  auto it =
233  std::find_if(instances.begin(), instances.end(),
234  [&](const auto &inst) { return instName == inst.name; });
235 
236  assert(it != instances.end() && "instance does not exist!");
237 
238  return it;
239 }
240 
241 int State::addSignal(std::string name, std::string owner) {
242  signals.push_back(Signal(name, owner));
243  return signals.size() - 1;
244 }
245 
246 void State::addProcPtr(std::string name, ProcState *procStatePtr) {
247  auto it = getInstanceIterator(name);
248 
249  // Store instance index in process state.
250  procStatePtr->inst = it - instances.begin();
251  (*it).procState = procStatePtr;
252 }
253 
254 int State::addSignalData(int index, std::string owner, uint8_t *value,
255  uint64_t size) {
256  auto it = getInstanceIterator(owner);
257 
258  uint64_t globalIdx = (*it).sensitivityList[index + (*it).nArgs].globalIndex;
259  auto &sig = signals[globalIdx];
260 
261  // Add pointer and size to global signal table entry.
262  sig.store(value, size);
263 
264  // Add the value pointer to the signal detail struct for each instance this
265  // signal appears in.
266  for (auto inst : signals[globalIdx].getTriggeredInstanceIndices()) {
267  for (auto &detail : instances[inst].sensitivityList) {
268  if (detail.globalIndex == globalIdx) {
269  detail.value = sig.getValue();
270  }
271  }
272  }
273  return globalIdx;
274 }
275 
276 void State::addSignalElement(unsigned index, unsigned offset, unsigned size) {
277  signals[index].pushElement(std::make_pair(offset, size));
278 }
279 
280 void State::dumpSignal(llvm::raw_ostream &out, int index) {
281  auto &sig = signals[index];
282  for (auto inst : sig.getTriggeredInstanceIndices()) {
283  out << time.toString() << " " << instances[inst].path << "/"
284  << sig.getName() << " " << sig.toHexString() << "\n";
285  }
286 }
287 
288 void State::dumpLayout() {
289  llvm::errs() << "::------------------- Layout -------------------::\n";
290  for (const auto &inst : instances) {
291  llvm::errs() << inst.name << ":\n";
292  llvm::errs() << "---path: " << inst.path << "\n";
293  llvm::errs() << "---isEntity: " << inst.isEntity << "\n";
294  llvm::errs() << "---sensitivity list: ";
295  for (auto in : inst.sensitivityList) {
296  llvm::errs() << in.globalIndex << " ";
297  }
298  llvm::errs() << "\n";
299  }
300  llvm::errs() << "::----------------------------------------------::\n";
301 }
302 
303 void State::dumpSignalTriggers() {
304  llvm::errs() << "::------------- Signal information -------------::\n";
305  for (size_t i = 0, e = signals.size(); i < e; ++i) {
306  llvm::errs() << signals[i].getOwner() << "/" << signals[i].getName()
307  << " triggers: ";
308  for (auto trig : signals[i].getTriggeredInstanceIndices()) {
309  llvm::errs() << trig << " ";
310  }
311  llvm::errs() << "\n";
312  }
313  llvm::errs() << "::----------------------------------------------::\n";
314 }
assert(baseType &&"element must be base type")
constexpr const char * toString(Flow flow)
Definition: FIRRTLOps.cpp:169
int32_t width
Definition: FIRRTL.cpp:36
The simulator's internal representation of a signal.
Definition: State.h:76
The simulator's internal representation of time.
Definition: State.h:30
uint64_t time
Simulation real time.
Definition: State.h:62
bool operator<(const AppID &a, const AppID &b)
Definition: Manifest.cpp:608
State structure for process persistence across suspension.
Definition: State.h:269
The simulator's internal representation of one queue slot.
Definition: State.h:205