CIRCT  19.0.0git
Engine.cpp
Go to the documentation of this file.
1 //===- Engine.cpp - Simulator Engine class implementation -------*- 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 Engine class.
10 //
11 //===----------------------------------------------------------------------===//
12 
15 
16 #include "mlir/ExecutionEngine/ExecutionEngine.h"
17 #include "mlir/IR/Builders.h"
18 
19 #include "llvm/Support/TargetSelect.h"
20 
21 using namespace circt::llhd::sim;
22 
24  llvm::raw_ostream &out, ModuleOp module,
25  llvm::function_ref<mlir::LogicalResult(mlir::ModuleOp)> mlirTransformer,
26  llvm::function_ref<llvm::Error(llvm::Module *)> llvmTransformer,
27  std::string root, TraceMode tm, ArrayRef<StringRef> sharedLibPaths)
28  : out(out), root(root), traceMode(tm) {
29  state = std::make_unique<State>();
30  state->root = root + '.' + root;
31 
33 
34  auto rootEntity = module.lookupSymbol<EntityOp>(root);
35 
36  // Insert explicit instantiation of the design root.
37  OpBuilder insertInst =
38  OpBuilder::atBlockEnd(&rootEntity.getBody().getBlocks().front());
39  insertInst.create<InstOp>(rootEntity.getBlocks().front().back().getLoc(),
40  std::nullopt, root, root, ArrayRef<Value>(),
41  ArrayRef<Value>());
42 
43  if (failed(mlirTransformer(module))) {
44  llvm::errs() << "failed to apply the MLIR passes\n";
45  exit(EXIT_FAILURE);
46  }
47 
48  this->module = module;
49 
50  llvm::InitializeNativeTarget();
51  llvm::InitializeNativeTargetAsmPrinter();
52 
53  mlir::ExecutionEngineOptions options;
54  options.transformer = llvmTransformer;
55  options.sharedLibPaths = sharedLibPaths;
56  auto maybeEngine = mlir::ExecutionEngine::create(this->module, options);
57  assert(maybeEngine && "failed to create JIT");
58  engine = std::move(*maybeEngine);
59 }
60 
61 Engine::~Engine() = default;
62 
63 void Engine::dumpStateLayout() { state->dumpLayout(); }
64 
65 void Engine::dumpStateSignalTriggers() { state->dumpSignalTriggers(); }
66 
67 int Engine::simulate(int n, uint64_t maxTime) {
68  assert(engine && "engine not found");
69  assert(state && "state not found");
70 
71  auto tm = static_cast<TraceMode>(traceMode);
72  Trace trace(state, out, tm);
73 
74  SmallVector<void *, 1> arg({&state});
75  // Initialize tbe simulation state.
76  auto invocationResult = engine->invokePacked("llhd_init", arg);
77  if (invocationResult) {
78  llvm::errs() << "Failed invocation of llhd_init: " << invocationResult;
79  return -1;
80  }
81 
82  if (traceMode != TraceMode::None) {
83  // Add changes for all the signals' initial values.
84  for (size_t i = 0, e = state->signals.size(); i < e; ++i) {
85  trace.addChange(i);
86  }
87  }
88 
89  // Add a dummy event to get the simulation started.
90  state->queue.push_back(Slot(Time()));
91  ++state->queue.events;
92 
93  // Keep track of the instances that need to wakeup.
94  llvm::SmallVector<unsigned, 8> wakeupQueue;
95 
96  // Add all instances to the wakeup queue for the first run and add the jitted
97  // function pointers to all of the instances to make them readily available.
98  for (size_t i = 0, e = state->instances.size(); i < e; ++i) {
99  wakeupQueue.push_back(i);
100  auto &inst = state->instances[i];
101  auto expectedFPtr = engine->lookupPacked(inst.unit);
102  if (!expectedFPtr) {
103  llvm::errs() << "Could not lookup " << inst.unit << "!\n";
104  return -1;
105  }
106  inst.unitFPtr = *expectedFPtr;
107  }
108 
109  int cycle = 0;
110  while (state->queue.events > 0) {
111  const auto &pop = state->queue.top();
112 
113  // Interrupt the simulation if a stop condition is met.
114  if ((n > 0 && cycle >= n) ||
115  (maxTime > 0 && pop.time.getTime() > maxTime)) {
116  break;
117  }
118 
119  // Update the simulation time.
120  state->time = pop.time;
121 
122  if (traceMode != TraceMode::None)
123  trace.flush();
124 
125  // Process signal changes.
126  size_t i = 0, e = pop.changesSize;
127  while (i < e) {
128  const auto sigIndex = pop.changes[i].first;
129  auto &curr = state->signals[sigIndex];
130  APInt buff(curr.getSize() * 8, 0);
131  llvm::LoadIntFromMemory(buff, curr.getValue(), curr.getSize());
132 
133  // Apply the changes to the buffer until we reach the next signal.
134  while (i < e && pop.changes[i].first == sigIndex) {
135  const auto &change = pop.buffers[pop.changes[i].second];
136  const auto offset = change.first;
137  const auto &drive = change.second;
138  if (drive.getBitWidth() < buff.getBitWidth())
139  buff.insertBits(drive, offset);
140  else
141  buff = drive;
142 
143  ++i;
144  }
145 
146  if (!curr.updateWhenChanged(buff.getRawData()))
147  continue;
148 
149  // Add sensitive instances.
150  for (auto inst : curr.getTriggeredInstanceIndices()) {
151  // Skip if the process is not currently sensible to the signal.
152  if (!state->instances[inst].isEntity) {
153  const auto &sensList = state->instances[inst].sensitivityList;
154  auto it = std::find_if(sensList.begin(), sensList.end(),
155  [sigIndex](const SignalDetail &sig) {
156  return sig.globalIndex == sigIndex;
157  });
158  if (sensList.end() != it &&
159  state->instances[inst].procState->senses[it - sensList.begin()] ==
160  0)
161  continue;
162 
163  // Invalidate scheduled wakeup
164  state->instances[inst].expectedWakeup = Time();
165  }
166  wakeupQueue.push_back(inst);
167  }
168 
169  // Dump the updated signal.
170  if (traceMode != TraceMode::None)
171  trace.addChange(sigIndex);
172  }
173 
174  // Add scheduled process resumes to the wakeup queue.
175  for (auto inst : pop.scheduled) {
176  if (state->time == state->instances[inst].expectedWakeup)
177  wakeupQueue.push_back(inst);
178  }
179 
180  state->queue.pop();
181 
182  std::sort(wakeupQueue.begin(), wakeupQueue.end());
183  wakeupQueue.erase(std::unique(wakeupQueue.begin(), wakeupQueue.end()),
184  wakeupQueue.end());
185 
186  // Run the instances present in the wakeup queue.
187  for (auto i : wakeupQueue) {
188  auto &inst = state->instances[i];
189  auto signalTable = inst.sensitivityList.data();
190 
191  // Gather the instance arguments for unit invocation.
192  SmallVector<void *, 3> args;
193  if (inst.isEntity)
194  args.assign({&state, &inst.entityState, &signalTable});
195  else {
196  args.assign({&state, &inst.procState, &signalTable});
197  }
198  // Run the unit.
199  (*inst.unitFPtr)(args.data());
200  }
201 
202  // Clear wakeup queue.
203  wakeupQueue.clear();
204  ++cycle;
205  }
206 
207  if (traceMode != TraceMode::None) {
208  // Flush any remainign changes
209  trace.flush(/*force=*/true);
210  }
211 
212  llvm::errs() << "Finished at " << state->time.toString() << " (" << cycle
213  << " cycles)\n";
214  return 0;
215 }
216 
217 void Engine::buildLayout(ModuleOp module) {
218  // Start from the root entity.
219  auto rootEntity = module.lookupSymbol<EntityOp>(root);
220  assert(rootEntity && "root entity not found!");
221 
222  // Build root instance, the parent and instance names are the same for the
223  // root.
224  Instance rootInst(state->root);
225  rootInst.unit = root;
226  rootInst.path = root;
227 
228  // Recursively walk the units starting at root.
229  walkEntity(rootEntity, rootInst);
230 
231  // The root is always an instance.
232  rootInst.isEntity = true;
233  // Store the root instance.
234  state->instances.push_back(std::move(rootInst));
235 
236  // Add triggers to signals.
237  for (size_t i = 0, e = state->instances.size(); i < e; ++i) {
238  auto &inst = state->instances[i];
239  for (auto trigger : inst.sensitivityList) {
240  state->signals[trigger.globalIndex].pushInstanceIndex(i);
241  }
242  }
243 }
244 
245 void Engine::walkEntity(EntityOp entity, Instance &child) {
246  entity.walk([&](Operation *op) {
247  assert(op);
248 
249  // Add a signal to the signal table.
250  if (auto sig = dyn_cast<SigOp>(op)) {
251  uint64_t index = state->addSignal(sig.getName().str(), child.name);
252  child.sensitivityList.push_back(
253  SignalDetail({nullptr, 0, child.sensitivityList.size(), index}));
254  }
255 
256  // Build (recursive) instance layout.
257  if (auto inst = dyn_cast<InstOp>(op)) {
258  // Skip self-recursion.
259  if (inst.getCallee() == child.name)
260  return;
261  if (auto e =
262  op->getParentOfType<ModuleOp>().lookupSymbol(inst.getCallee())) {
263  Instance newChild(child.unit + '.' + inst.getName().str());
264  newChild.unit = inst.getCallee().str();
265  newChild.nArgs = inst.getNumOperands();
266  newChild.path = child.path + "/" + inst.getName().str();
267 
268  // Add instance arguments to sensitivity list. The first nArgs signals
269  // in the sensitivity list represent the unit's arguments, while the
270  // following ones represent the unit-defined signals.
271  llvm::SmallVector<Value, 8> args;
272  args.insert(args.end(), inst.getInputs().begin(),
273  inst.getInputs().end());
274  args.insert(args.end(), inst.getOutputs().begin(),
275  inst.getOutputs().end());
276 
277  for (size_t i = 0, e = args.size(); i < e; ++i) {
278  // The signal comes from an instance's argument.
279  if (auto blockArg = dyn_cast<BlockArgument>(args[i])) {
280  auto detail = child.sensitivityList[blockArg.getArgNumber()];
281  detail.instIndex = i;
282  newChild.sensitivityList.push_back(detail);
283  } else if (auto sigOp = dyn_cast<SigOp>(args[i].getDefiningOp())) {
284  // The signal comes from one of the instance's owned signals.
285  auto it = std::find_if(
286  child.sensitivityList.begin(), child.sensitivityList.end(),
287  [&](SignalDetail &detail) {
288  return state->signals[detail.globalIndex].getName() ==
289  sigOp.getName() &&
290  state->signals[detail.globalIndex].getOwner() ==
291  child.name;
292  });
293  if (it != child.sensitivityList.end()) {
294  auto detail = *it;
295  detail.instIndex = i;
296  newChild.sensitivityList.push_back(detail);
297  }
298  }
299  }
300 
301  // Recursively walk a new entity, otherwise it is a process and cannot
302  // define new signals or instances.
303  if (auto ent = dyn_cast<EntityOp>(e)) {
304  newChild.isEntity = true;
305  walkEntity(ent, newChild);
306  } else {
307  newChild.isEntity = false;
308  }
309 
310  // Store the created instance.
311  state->instances.push_back(std::move(newChild));
312  }
313  }
314  });
315 }
assert(baseType &&"element must be base type")
void dumpStateLayout()
Dump the instance layout stored in the State.
Definition: Engine.cpp:63
void walkEntity(EntityOp entity, Instance &child)
Definition: Engine.cpp:245
Engine(llvm::raw_ostream &out, ModuleOp module, llvm::function_ref< mlir::LogicalResult(mlir::ModuleOp)> mlirTransformer, llvm::function_ref< llvm::Error(llvm::Module *)> llvmTransformer, std::string root, TraceMode tm, ArrayRef< StringRef > sharedLibPaths)
Initialize an LLHD simulation engine.
Definition: Engine.cpp:23
std::string root
Definition: Engine.h:71
TraceMode traceMode
Definition: Engine.h:75
llvm::raw_ostream & out
Definition: Engine.h:70
~Engine()
Default destructor.
void buildLayout(ModuleOp module)
Build the instance layout of the design.
Definition: Engine.cpp:217
std::unique_ptr< mlir::ExecutionEngine > engine
Definition: Engine.h:73
std::unique_ptr< State > state
Definition: Engine.h:72
int simulate(int n, uint64_t maxTime)
Run simulation up to n steps or maxTime picoseconds of simulation time.
Definition: Engine.cpp:67
void dumpStateSignalTriggers()
Dump the instances each signal triggers.
Definition: Engine.cpp:65
The simulator's internal representation of time.
Definition: State.h:30
void flush(bool force=false)
Flush the changes buffer to the output stream.
Definition: Trace.cpp:121
void addChange(unsigned)
Add a value change to the trace changes buffer.
Definition: Trace.cpp:75
The simulator internal representation of an instance.
Definition: State.h:277
llvm::SmallVector< SignalDetail, 0 > sensitivityList
Definition: State.h:292
Detail structure that can be easily accessed by the lowered code.
Definition: State.h:68
The simulator's internal representation of one queue slot.
Definition: State.h:205