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