CIRCT  19.0.0git
ESIServices.cpp
Go to the documentation of this file.
1 //===- ESIServices.cpp - Code related to ESI services ---------------------===//
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 #include "PassDetails.h"
10 
16 #include "circt/Dialect/HW/HWOps.h"
18 #include "circt/Dialect/SV/SVOps.h"
21 
22 #include "mlir/IR/BuiltinTypes.h"
23 #include "mlir/IR/ImplicitLocOpBuilder.h"
24 
25 #include <memory>
26 #include <utility>
27 
28 using namespace circt;
29 using namespace circt::esi;
30 
31 LogicalResult
32 ServiceGeneratorDispatcher::generate(ServiceImplementReqOp req,
33  ServiceDeclOpInterface decl) {
34  // Lookup based on 'impl_type' attribute and pass through the generate request
35  // if found.
36  auto genF = genLookupTable.find(req.getImplTypeAttr().getValue());
37  if (genF == genLookupTable.end()) {
38  if (failIfNotFound)
39  return req.emitOpError("Could not find service generator for attribute '")
40  << req.getImplTypeAttr() << "'";
41  return success();
42  }
43 
44  // Since we always need a record of generation, create it here then pass it to
45  // the generator for possible modification.
46  OpBuilder b(req);
47  auto implRecord = b.create<ServiceImplRecordOp>(
48  req.getLoc(), req.getAppID(), req.getServiceSymbolAttr(),
49  req.getStdServiceAttr(), req.getImplTypeAttr(), b.getDictionaryAttr({}));
50  implRecord.getReqDetails().emplaceBlock();
51 
52  return genF->second(req, decl, implRecord);
53 }
54 
55 /// The generator for the "cosim" impl_type.
56 static LogicalResult
57 instantiateCosimEndpointOps(ServiceImplementReqOp implReq,
58  ServiceDeclOpInterface,
59  ServiceImplRecordOp implRecord) {
60  auto *ctxt = implReq.getContext();
61  OpBuilder b(implReq);
62  Value clk = implReq.getOperand(0);
63  Value rst = implReq.getOperand(1);
64 
65  if (implReq.getImplOpts()) {
66  auto opts = implReq.getImplOpts()->getValue();
67  for (auto nameAttr : opts) {
68  return implReq.emitOpError("did not recognize option name ")
69  << nameAttr.getName();
70  }
71  }
72 
73  Block &connImplBlock = implRecord.getReqDetails().front();
74  OpBuilder implRecords = OpBuilder::atBlockEnd(&connImplBlock);
75 
76  // Assemble the name to use for an endpoint.
77  auto toStringAttr = [&](ArrayAttr strArr, StringAttr channelName) {
78  std::string buff;
79  llvm::raw_string_ostream os(buff);
80  llvm::interleave(
81  strArr.getAsRange<AppIDAttr>(), os,
82  [&](AppIDAttr appid) {
83  os << appid.getName().getValue();
84  if (appid.getIndex())
85  os << "[" << appid.getIndex() << "]";
86  },
87  ".");
88  os << "." << channelName.getValue();
89  return StringAttr::get(ctxt, os.str());
90  };
91 
92  llvm::DenseMap<ServiceImplementConnReqOp, unsigned> toClientResultNum;
93  for (auto req : implReq.getOps<ServiceImplementConnReqOp>())
94  toClientResultNum[req] = toClientResultNum.size();
95 
96  // Iterate through the requests, building a cosim endpoint for each channel in
97  // the bundle.
98  // TODO: The cosim op should probably be able to take a bundle type and get
99  // lowered to the SV primitive later on. The SV primitive will also need some
100  // work to suit this new world order, so let's put this off.
101  for (auto req : implReq.getOps<ServiceImplementConnReqOp>()) {
102  Location loc = req->getLoc();
103  ChannelBundleType bundleType = req.getToClient().getType();
104  SmallVector<NamedAttribute, 8> channelAssignments;
105 
106  SmallVector<Value, 8> toServerValues;
107  for (BundledChannel ch : bundleType.getChannels()) {
108  if (ch.direction == ChannelDirection::to) {
109  auto cosim = b.create<CosimFromHostEndpointOp>(
110  loc, ch.type, clk, rst,
111  toStringAttr(req.getRelativeAppIDPathAttr(), ch.name));
112  toServerValues.push_back(cosim.getFromHost());
113  channelAssignments.push_back(
114  b.getNamedAttr(ch.name, cosim.getIdAttr()));
115  }
116  }
117 
118  auto pack =
119  b.create<PackBundleOp>(implReq.getLoc(), bundleType, toServerValues);
120  implReq.getResult(toClientResultNum[req])
121  .replaceAllUsesWith(pack.getBundle());
122 
123  size_t chanIdx = 0;
124  for (BundledChannel ch : bundleType.getChannels()) {
125  if (ch.direction == ChannelDirection::from) {
126  auto cosim = b.create<CosimToHostEndpointOp>(
127  loc, clk, rst, pack.getFromChannels()[chanIdx++],
128  toStringAttr(req.getRelativeAppIDPathAttr(), ch.name));
129  channelAssignments.push_back(
130  b.getNamedAttr(ch.name, cosim.getIdAttr()));
131  }
132  }
133 
134  implRecords.create<ServiceImplClientRecordOp>(
135  req.getLoc(), req.getRelativeAppIDPathAttr(), req.getServicePortAttr(),
136  TypeAttr::get(bundleType),
137  b.getDictionaryAttr(b.getNamedAttr(
138  "channel_assignments", b.getDictionaryAttr(channelAssignments))));
139  }
140 
141  // Erase the generation request.
142  implReq.erase();
143  return success();
144 }
145 
146 // Generator for "sv_mem" implementation type. Emits SV ops for an unpacked
147 // array, hopefully inferred as a memory to the SV compiler.
148 static LogicalResult
149 instantiateSystemVerilogMemory(ServiceImplementReqOp implReq,
150  ServiceDeclOpInterface decl,
151  ServiceImplRecordOp) {
152  if (!decl)
153  return implReq.emitOpError(
154  "Must specify a service declaration to use 'sv_mem'.");
155 
156  ImplicitLocOpBuilder b(implReq.getLoc(), implReq);
157  BackedgeBuilder bb(b, implReq.getLoc());
158 
159  RandomAccessMemoryDeclOp ramDecl =
160  dyn_cast<RandomAccessMemoryDeclOp>(decl.getOperation());
161  if (!ramDecl)
162  return implReq.emitOpError(
163  "'sv_mem' implementation type can only be used to "
164  "implement RandomAccessMemory declarations");
165 
166  if (implReq.getNumOperands() != 2)
167  return implReq.emitOpError("Implementation requires clk and rst operands");
168  auto clk = implReq.getOperand(0);
169  auto rst = implReq.getOperand(1);
170  auto write = b.getStringAttr("write");
171  auto read = b.getStringAttr("read");
172  auto none = b.create<hw::ConstantOp>(
173  APInt(/*numBits*/ 0, /*val*/ 0, /*isSigned*/ false));
174  auto i1 = b.getI1Type();
175  auto c0 = b.create<hw::ConstantOp>(i1, 0);
176 
177  // List of reqs which have a result.
178  SmallVector<ServiceImplementConnReqOp, 8> toClientReqs(
179  llvm::make_filter_range(
180  implReq.getOps<ServiceImplementConnReqOp>(),
181  [](auto req) { return req.getToClient() != nullptr; }));
182 
183  // Assemble a mapping of toClient results to actual consumers.
184  DenseMap<Value, Value> outputMap;
185  for (auto [bout, reqout] :
186  llvm::zip_longest(toClientReqs, implReq.getResults())) {
187  assert(bout.has_value());
188  assert(reqout.has_value());
189  Value toClient = bout->getToClient();
190  outputMap[toClient] = *reqout;
191  }
192 
193  // Create the SV memory.
194  hw::UnpackedArrayType memType =
195  hw::UnpackedArrayType::get(ramDecl.getInnerType(), ramDecl.getDepth());
196  auto mem =
197  b.create<sv::RegOp>(memType, implReq.getServiceSymbolAttr().getAttr())
198  .getResult();
199 
200  // Do everything which doesn't actually write to the memory, store the signals
201  // needed for the actual memory writes for later.
202  SmallVector<std::tuple<Value, Value, Value>> writeGoAddressData;
203  for (auto req : implReq.getOps<ServiceImplementConnReqOp>()) {
204  auto port = req.getServicePort().getName();
205  Value toClientResp;
206 
207  if (port == write) {
208  // If this pair is doing a write...
209 
210  // Construct the response channel.
211  auto doneValid = bb.get(i1);
212  auto ackChannel = b.create<WrapValidReadyOp>(none, doneValid);
213 
214  auto pack =
215  b.create<PackBundleOp>(implReq.getLoc(), req.getToClient().getType(),
216  ackChannel.getChanOutput());
217  Value toServer =
218  pack.getFromChannels()[RandomAccessMemoryDeclOp::ReqDirChannelIdx];
219  toClientResp = pack.getBundle();
220 
221  // Unwrap the write request and 'explode' the struct.
222  auto unwrap =
223  b.create<UnwrapValidReadyOp>(toServer, ackChannel.getReady());
224 
225  Value address = b.create<hw::StructExtractOp>(unwrap.getRawOutput(),
226  b.getStringAttr("address"));
227  Value data = b.create<hw::StructExtractOp>(unwrap.getRawOutput(),
228  b.getStringAttr("data"));
229 
230  // Determine if the write should occur this cycle.
231  auto go = b.create<comb::AndOp>(unwrap.getValid(), unwrap.getReady());
232  go->setAttr("sv.namehint", b.getStringAttr("write_go"));
233  // Register the 'go' signal and use it as the done message.
234  doneValid.setValue(
235  b.create<seq::CompRegOp>(go, clk, rst, c0, "write_done"));
236  // Store the necessary data for the 'always' memory writing block.
237  writeGoAddressData.push_back(std::make_tuple(go, address, data));
238 
239  } else if (port == read) {
240  // If it's a read...
241 
242  // Construct the response channel.
243  auto dataValid = bb.get(i1);
244  auto data = bb.get(ramDecl.getInnerType());
245  auto dataChannel = b.create<WrapValidReadyOp>(data, dataValid);
246 
247  auto pack =
248  b.create<PackBundleOp>(implReq.getLoc(), req.getToClient().getType(),
249  dataChannel.getChanOutput());
250  Value toServer =
251  pack.getFromChannels()[RandomAccessMemoryDeclOp::RespDirChannelIdx];
252  toClientResp = pack.getBundle();
253 
254  // Unwrap the requested address and read from that memory location.
255  auto addressUnwrap =
256  b.create<UnwrapValidReadyOp>(toServer, dataChannel.getReady());
257  Value memLoc =
258  b.create<sv::ArrayIndexInOutOp>(mem, addressUnwrap.getRawOutput());
259  auto readData = b.create<sv::ReadInOutOp>(memLoc);
260 
261  // Set the data on the response.
262  data.setValue(readData);
263  dataValid.setValue(addressUnwrap.getValid());
264  } else {
265  assert(false && "Port should be either 'read' or 'write'");
266  }
267 
268  outputMap[req.getToClient()].replaceAllUsesWith(toClientResp);
269  }
270 
271  // Now construct the memory writes.
272  auto hwClk = b.create<seq::FromClockOp>(clk);
273  b.create<sv::AlwaysFFOp>(
274  sv::EventControl::AtPosEdge, hwClk, ResetType::SyncReset,
275  sv::EventControl::AtPosEdge, rst, [&] {
276  for (auto [go, address, data] : writeGoAddressData) {
277  Value a = address, d = data; // So the lambda can capture.
278  // If we're told to go, do the write.
279  b.create<sv::IfOp>(go, [&] {
280  Value memLoc = b.create<sv::ArrayIndexInOutOp>(mem, a);
281  b.create<sv::PAssignOp>(memLoc, d);
282  });
283  }
284  });
285 
286  implReq.erase();
287  return success();
288 }
289 
291  DenseMap<StringRef, ServiceGeneratorDispatcher::ServiceGeneratorFunc>{
292  {"cosim", instantiateCosimEndpointOps},
293  {"sv_mem", instantiateSystemVerilogMemory}},
294  false);
295 
298 }
299 
301  ServiceGeneratorFunc gen) {
302  genLookupTable[implType] = std::move(gen);
303 }
304 
305 //===----------------------------------------------------------------------===//
306 // Wire up services pass.
307 //===----------------------------------------------------------------------===//
308 
309 namespace {
310 /// Implements a pass to connect up ESI services clients to the nearest server
311 /// instantiation. Wires up the ports and generates a generation request to
312 /// call a user-specified generator.
313 struct ESIConnectServicesPass
314  : public ESIConnectServicesBase<ESIConnectServicesPass>,
316 
317  ESIConnectServicesPass(const ServiceGeneratorDispatcher &gen)
318  : genDispatcher(gen) {}
319  ESIConnectServicesPass()
320  : genDispatcher(ServiceGeneratorDispatcher::globalDispatcher()) {}
321 
322  void runOnOperation() override;
323 
324  /// Convert connection requests to service implement connection requests,
325  /// which have a relative appid path instead of just an appid. Leave being a
326  /// record for the manifest of the original request.
327  void convertReq(RequestConnectionOp);
328 
329  /// "Bubble up" the specified requests to all of the instantiations of the
330  /// module specified. Create and connect up ports to tunnel the ESI channels
331  /// through.
332  LogicalResult surfaceReqs(hw::HWMutableModuleLike,
333  ArrayRef<ServiceImplementConnReqOp>);
334 
335  /// For any service which is "local" (provides the requested service) in a
336  /// module, replace it with a ServiceImplementOp. Said op is to be replaced
337  /// with an instantiation by a generator.
338  LogicalResult replaceInst(ServiceInstanceOp, Block *portReqs);
339 
340  /// Figure out which requests are "local" vs need to be surfaced. Call
341  /// 'surfaceReqs' and/or 'replaceInst' as appropriate.
342  LogicalResult process(hw::HWModuleLike);
343 
344  /// If the servicePort is referring to a std service, return the name of it.
345  StringAttr getStdService(FlatSymbolRefAttr serviceSym);
346 
347 private:
348  ServiceGeneratorDispatcher genDispatcher;
349 };
350 } // anonymous namespace
351 
352 void ESIConnectServicesPass::runOnOperation() {
353  ModuleOp outerMod = getOperation();
354  topLevelSyms.addDefinitions(outerMod);
355 
356  outerMod.walk([&](RequestConnectionOp req) { convertReq(req); });
357 
358  // Get a partially-ordered list of modules based on the instantiation DAG.
359  // It's _very_ important that we process modules before their instantiations
360  // so that the modules where they're instantiated correctly process the
361  // surfaced connections.
362  SmallVector<hw::HWModuleLike, 64> sortedMods;
363  getAndSortModules(outerMod, sortedMods);
364 
365  // Process each module.
366  for (auto mod : sortedMods) {
367  hw::HWModuleLike mutableMod = dyn_cast<hw::HWModuleLike>(*mod);
368  if (mutableMod && failed(process(mutableMod))) {
369  signalPassFailure();
370  return;
371  }
372  }
373 }
374 
375 // Get the std service name, if any.
376 StringAttr ESIConnectServicesPass::getStdService(FlatSymbolRefAttr svcSym) {
377  if (!svcSym)
378  return {};
379  Operation *svcDecl = topLevelSyms.getDefinition(svcSym);
380  if (!isa<CustomServiceDeclOp>(svcDecl))
381  return svcDecl->getName().getIdentifier();
382  return {};
383 }
384 
385 void ESIConnectServicesPass::convertReq(RequestConnectionOp req) {
386  OpBuilder b(req);
387  auto newReq = b.create<ServiceImplementConnReqOp>(
388  req.getLoc(), req.getToClient().getType(), req.getServicePortAttr(),
389  ArrayAttr::get(&getContext(), {req.getAppIDAttr()}));
390  newReq->setDialectAttrs(req->getDialectAttrs());
391  req.getToClient().replaceAllUsesWith(newReq.getToClient());
392 
393  // Emit a record of the original request.
394  b.create<ServiceRequestRecordOp>(
395  req.getLoc(), req.getAppID(), req.getServicePortAttr(),
396  getStdService(req.getServicePortAttr().getModuleRef()),
397  req.getToClient().getType());
398  req.erase();
399 }
400 
401 LogicalResult ESIConnectServicesPass::process(hw::HWModuleLike mod) {
402  // If 'mod' doesn't have a body, assume it's an external module.
403  if (mod->getNumRegions() == 0 || mod->getRegion(0).empty())
404  return success();
405 
406  Block &modBlock = mod->getRegion(0).front();
407 
408  // The non-local reqs which need to be surfaced from this module.
409  SmallVector<ServiceImplementConnReqOp, 4> nonLocalReqs;
410  // Index the local services and create blocks in which to put the requests.
411  DenseMap<SymbolRefAttr, Block *> localImplReqs;
412  Block *anyServiceInst = nullptr;
413  for (auto instOp : modBlock.getOps<ServiceInstanceOp>()) {
414  auto *b = new Block();
415  localImplReqs[instOp.getServiceSymbolAttr()] = b;
416  if (!instOp.getServiceSymbolAttr())
417  anyServiceInst = b;
418  }
419 
420  // Sort the various requests by destination.
421  mod.walk([&](ServiceImplementConnReqOp req) {
422  auto service = req.getServicePort().getModuleRef();
423  auto implOpF = localImplReqs.find(service);
424  if (implOpF != localImplReqs.end())
425  req->moveBefore(implOpF->second, implOpF->second->end());
426  else if (anyServiceInst)
427  req->moveBefore(anyServiceInst, anyServiceInst->end());
428  else
429  nonLocalReqs.push_back(req);
430  });
431 
432  // Replace each service instance with a generation request. If a service
433  // generator is registered, generate the server.
434  for (auto instOp :
435  llvm::make_early_inc_range(modBlock.getOps<ServiceInstanceOp>())) {
436  Block *portReqs = localImplReqs[instOp.getServiceSymbolAttr()];
437  if (failed(replaceInst(instOp, portReqs)))
438  return failure();
439  }
440 
441  // Surface all of the requests which cannot be fulfilled locally.
442  if (nonLocalReqs.empty())
443  return success();
444 
445  if (auto mutableMod = dyn_cast<hw::HWMutableModuleLike>(mod.getOperation()))
446  return surfaceReqs(mutableMod, nonLocalReqs);
447  return mod.emitOpError(
448  "Cannot surface requests through module without mutable ports");
449 }
450 
451 LogicalResult ESIConnectServicesPass::replaceInst(ServiceInstanceOp instOp,
452  Block *portReqs) {
453  assert(portReqs);
454  auto declSym = instOp.getServiceSymbolAttr();
455  ServiceDeclOpInterface decl;
456  if (declSym) {
457  decl = dyn_cast_or_null<ServiceDeclOpInterface>(
458  topLevelSyms.getDefinition(declSym));
459  if (!decl)
460  return instOp.emitOpError("Could not find service declaration ")
461  << declSym;
462  }
463 
464  // Compute the result types for the new op -- the instance op's output types
465  // + the to_client types.
466  SmallVector<Type, 8> resultTypes(instOp.getResultTypes().begin(),
467  instOp.getResultTypes().end());
468  for (auto req : portReqs->getOps<ServiceImplementConnReqOp>())
469  resultTypes.push_back(req.getBundleType());
470 
471  // Create the generation request op.
472  OpBuilder b(instOp);
473  auto implOp = b.create<ServiceImplementReqOp>(
474  instOp.getLoc(), resultTypes, instOp.getAppIDAttr(),
475  instOp.getServiceSymbolAttr(), instOp.getImplTypeAttr(),
476  getStdService(declSym), instOp.getImplOptsAttr(), instOp.getOperands());
477  implOp->setDialectAttrs(instOp->getDialectAttrs());
478  implOp.getPortReqs().push_back(portReqs);
479 
480  // Update the users.
481  for (auto [n, o] : llvm::zip(implOp.getResults(), instOp.getResults()))
482  o.replaceAllUsesWith(n);
483  unsigned instOpNumResults = instOp.getNumResults();
484  for (auto [idx, req] :
485  llvm::enumerate(portReqs->getOps<ServiceImplementConnReqOp>())) {
486  req.getToClient().replaceAllUsesWith(
487  implOp.getResult(idx + instOpNumResults));
488  }
489 
490  // Try to generate the service provider.
491  if (failed(genDispatcher.generate(implOp, decl)))
492  return instOp.emitOpError("failed to generate server");
493 
494  instOp.erase();
495  return success();
496 }
497 
498 LogicalResult
499 ESIConnectServicesPass::surfaceReqs(hw::HWMutableModuleLike mod,
500  ArrayRef<ServiceImplementConnReqOp> reqs) {
501  auto *ctxt = mod.getContext();
502  Block *body = &mod->getRegion(0).front();
503 
504  // Track initial operand/result counts and the new IO.
505  unsigned origNumInputs = mod.getNumInputPorts();
506  SmallVector<std::pair<unsigned, hw::PortInfo>> newInputs;
507 
508  // Assemble a port name from an array.
509  auto getPortName = [&](ArrayAttr namePath) {
510  std::string portName;
511  llvm::raw_string_ostream nameOS(portName);
512  llvm::interleave(
513  namePath.getAsRange<AppIDAttr>(), nameOS,
514  [&](AppIDAttr appid) {
515  nameOS << appid.getName().getValue();
516  if (appid.getIndex())
517  nameOS << "_" << appid.getIndex();
518  },
519  ".");
520  return StringAttr::get(ctxt, nameOS.str());
521  };
522 
523  for (auto req : reqs)
524  if (req->getParentWithTrait<OpTrait::IsIsolatedFromAbove>() != mod)
525  return req.emitOpError(
526  "Cannot surface requests through isolated from above ops");
527 
528  // Insert new module input ESI ports.
529  for (auto req : reqs) {
530  newInputs.push_back(std::make_pair(
531  origNumInputs,
532  hw::PortInfo{{getPortName(req.getRelativeAppIDPathAttr()),
533  req.getBundleType(), hw::ModulePort::Direction::Input},
534  origNumInputs,
535  {},
536  req->getLoc()}));
537 
538  // Replace uses with new block args which will correspond to said ports.
539  Value replValue = body->addArgument(req.getBundleType(), req->getLoc());
540  req.getToClient().replaceAllUsesWith(replValue);
541  }
542  mod.insertPorts(newInputs, {});
543 
544  // Prepend a name to the instance tracking array.
545  auto prependNamePart = [&](ArrayAttr appIDPath, AppIDAttr appID) {
546  SmallVector<Attribute, 8> newAppIDPath;
547  newAppIDPath.push_back(appID);
548  newAppIDPath.append(appIDPath.begin(), appIDPath.end());
549  return ArrayAttr::get(appIDPath.getContext(), newAppIDPath);
550  };
551 
552  // Update the module instantiations.
553  SmallVector<igraph::InstanceOpInterface, 1> newModuleInstantiations;
554  for (auto inst : moduleInstantiations[mod]) {
555  OpBuilder b(inst);
556 
557  // Add new inputs for the new bundles being requested.
558  SmallVector<Value, 16> newOperands;
559  for (auto req : reqs) {
560  // If the instance has an AppID, prepend it.
561  ArrayAttr appIDPath = req.getRelativeAppIDPathAttr();
562  if (auto instAppID = dyn_cast_or_null<AppIDAttr>(
563  inst->getDiscardableAttr(AppIDAttr::AppIDAttrName)))
564  appIDPath = prependNamePart(appIDPath, instAppID);
565 
566  // Clone the request.
567  auto clone = b.create<ServiceImplementConnReqOp>(
568  req.getLoc(), req.getToClient().getType(), req.getServicePortAttr(),
569  appIDPath);
570  clone->setDialectAttrs(req->getDialectAttrs());
571  newOperands.push_back(clone.getToClient());
572  }
573  inst->insertOperands(inst->getNumOperands(), newOperands);
574  // Set the names, if we know how.
575  if (auto hwInst = dyn_cast<hw::InstanceOp>(*inst))
576  hwInst.setArgNamesAttr(b.getArrayAttr(mod.getInputNames()));
577  }
578 
579  // Erase the original requests since they have been cloned into the proper
580  // destination modules.
581  for (auto req : reqs)
582  req.erase();
583  return success();
584 }
585 
586 std::unique_ptr<OperationPass<ModuleOp>>
588  return std::make_unique<ESIConnectServicesPass>();
589 }
assert(baseType &&"element must be base type")
static ServiceGeneratorDispatcher globalDispatcher(DenseMap< StringRef, ServiceGeneratorDispatcher::ServiceGeneratorFunc >{ {"cosim", instantiateCosimEndpointOps}, {"sv_mem", instantiateSystemVerilogMemory}}, false)
static LogicalResult instantiateCosimEndpointOps(ServiceImplementReqOp implReq, ServiceDeclOpInterface, ServiceImplRecordOp implRecord)
The generator for the "cosim" impl_type.
Definition: ESIServices.cpp:57
static LogicalResult instantiateSystemVerilogMemory(ServiceImplementReqOp implReq, ServiceDeclOpInterface decl, ServiceImplRecordOp)
@ Input
Definition: HW.h:35
static EvaluatorValuePtr unwrap(OMEvaluatorValue c)
Definition: OM.cpp:96
Instantiate one of these and use it to build typed backedges.
Backedge get(mlir::Type resultType, mlir::LocationAttr optionalLoc={})
Create a typed backedge.
Class which "dispatches" a service implementation request to its specified generator.
Definition: ESIServices.h:24
void registerGenerator(StringRef implType, ServiceGeneratorFunc gen)
Add a generator to this registry.
LogicalResult generate(ServiceImplementReqOp, ServiceDeclOpInterface)
Generate a service implementation if a generator exists in this registry.
Definition: ESIServices.cpp:32
static ServiceGeneratorDispatcher & globalDispatcher()
Get the global dispatcher.
DenseMap< StringRef, ServiceGeneratorFunc > genLookupTable
Definition: ESIServices.h:52
std::function< LogicalResult(ServiceImplementReqOp, ServiceDeclOpInterface, ServiceImplRecordOp)> ServiceGeneratorFunc
Definition: ESIServices.h:28
def create(data_type, value)
Definition: hw.py:393
def create(struct_value, str field_name)
Definition: hw.py:516
Definition: sv.py:68
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:54
std::unique_ptr< OperationPass< ModuleOp > > createESIConnectServicesPass()
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
A set of methods which are broadly useful in a number of dialects.
Definition: MSFTPasses.h:30