16 #include "mlir/ExecutionEngine/ExecutionEngine.h"
17 #include "mlir/IR/Builders.h"
19 #include "llvm/Support/TargetSelect.h"
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>();
34 auto rootEntity =
module.lookupSymbol<EntityOp>(
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>(),
43 if (failed(mlirTransformer(
module))) {
44 llvm::errs() <<
"failed to apply the MLIR passes\n";
50 llvm::InitializeNativeTarget();
51 llvm::InitializeNativeTargetAsmPrinter();
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);
74 SmallVector<void *, 1> arg({&
state});
76 auto invocationResult =
engine->invokePacked(
"llhd_init", arg);
77 if (invocationResult) {
78 llvm::errs() <<
"Failed invocation of llhd_init: " << invocationResult;
84 for (
size_t i = 0, e =
state->signals.size(); i < e; ++i) {
91 ++
state->queue.events;
94 llvm::SmallVector<unsigned, 8> wakeupQueue;
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);
103 llvm::errs() <<
"Could not lookup " << inst.unit <<
"!\n";
106 inst.unitFPtr = *expectedFPtr;
110 while (
state->queue.events > 0) {
111 const auto &pop =
state->queue.top();
114 if ((n > 0 && cycle >= n) ||
115 (maxTime > 0 && pop.time.getTime() > maxTime)) {
120 state->time = pop.time;
126 size_t i = 0, e = pop.changesSize;
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());
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);
146 if (!curr.updateWhenChanged(buff.getRawData()))
150 for (
auto inst : curr.getTriggeredInstanceIndices()) {
152 if (!
state->instances[inst].isEntity) {
153 const auto &sensList =
state->instances[inst].sensitivityList;
154 auto it = std::find_if(sensList.begin(), sensList.end(),
156 return sig.globalIndex == sigIndex;
158 if (sensList.end() != it &&
159 state->instances[inst].procState->senses[it - sensList.begin()] ==
164 state->instances[inst].expectedWakeup =
Time();
166 wakeupQueue.push_back(inst);
175 for (
auto inst : pop.scheduled) {
176 if (
state->time ==
state->instances[inst].expectedWakeup)
177 wakeupQueue.push_back(inst);
182 std::sort(wakeupQueue.begin(), wakeupQueue.end());
183 wakeupQueue.erase(std::unique(wakeupQueue.begin(), wakeupQueue.end()),
187 for (
auto i : wakeupQueue) {
188 auto &inst =
state->instances[i];
189 auto signalTable = inst.sensitivityList.data();
192 SmallVector<void *, 3> args;
194 args.assign({&
state, &inst.entityState, &signalTable});
196 args.assign({&
state, &inst.procState, &signalTable});
199 (*inst.unitFPtr)(args.data());
212 llvm::errs() <<
"Finished at " <<
state->time.toString() <<
" (" << cycle
219 auto rootEntity =
module.lookupSymbol<EntityOp>(
root);
220 assert(rootEntity &&
"root entity not found!");
234 state->instances.push_back(std::move(rootInst));
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);
246 entity.walk([&](Operation *op) {
250 if (
auto sig = dyn_cast<SigOp>(op)) {
251 uint64_t index =
state->addSignal(sig.getName().str(), child.
name);
257 if (
auto inst = dyn_cast<InstOp>(op)) {
259 if (inst.getCallee() == child.
name)
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();
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());
277 for (
size_t i = 0, e = args.size(); i < e; ++i) {
279 if (
auto blockArg = args[i].dyn_cast<BlockArgument>()) {
283 }
else if (
auto sigOp = dyn_cast<SigOp>(args[i].getDefiningOp())) {
285 auto it = std::find_if(
288 return state->signals[detail.globalIndex].getName() ==
290 state->signals[detail.globalIndex].getOwner() ==
303 if (
auto ent = dyn_cast<EntityOp>(e)) {
311 state->instances.push_back(std::move(newChild));
assert(baseType &&"element must be base type")
void dumpStateLayout()
Dump the instance layout stored in the State.
void walkEntity(EntityOp entity, Instance &child)
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.
~Engine()
Default destructor.
void buildLayout(ModuleOp module)
Build the instance layout of the design.
std::unique_ptr< mlir::ExecutionEngine > engine
std::unique_ptr< State > state
int simulate(int n, uint64_t maxTime)
Run simulation up to n steps or maxTime picoseconds of simulation time.
void dumpStateSignalTriggers()
Dump the instances each signal triggers.
The simulator's internal representation of time.
void flush(bool force=false)
Flush the changes buffer to the output stream.
void addChange(unsigned)
Add a value change to the trace changes buffer.
mlir::raw_indented_ostream & errs()
The simulator internal representation of an instance.
llvm::SmallVector< SignalDetail, 0 > sensitivityList
Detail structure that can be easily accessed by the lowered code.
The simulator's internal representation of one queue slot.