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