CIRCT  19.0.0git
ESIOps.cpp
Go to the documentation of this file.
1 //===- ESIOps.cpp - ESI op code defs ----------------------------*- 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 is where op definitions live.
10 //
11 //===----------------------------------------------------------------------===//
12 
16 #include "circt/Dialect/SV/SVOps.h"
18 #include "circt/Support/LLVM.h"
19 
20 #include "mlir/IR/Builders.h"
21 #include "mlir/IR/BuiltinTypes.h"
22 #include "mlir/IR/PatternMatch.h"
23 #include "mlir/IR/SymbolTable.h"
24 
25 using namespace circt;
26 using namespace circt::esi;
27 
28 //===----------------------------------------------------------------------===//
29 // ChannelBufferOp functions.
30 //===----------------------------------------------------------------------===//
31 
32 ParseResult ChannelBufferOp::parse(OpAsmParser &parser,
33  OperationState &result) {
34  llvm::SMLoc inputOperandsLoc = parser.getCurrentLocation();
35 
36  llvm::SmallVector<OpAsmParser::UnresolvedOperand, 4> operands;
37  if (parser.parseOperandList(operands, /*requiredOperandCount=*/3,
38  /*delimiter=*/OpAsmParser::Delimiter::None))
39  return failure();
40 
41  Type innerOutputType;
42  if (parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
43  parser.parseType(innerOutputType))
44  return failure();
45  auto outputType =
46  ChannelType::get(parser.getBuilder().getContext(), innerOutputType);
47  result.addTypes({outputType});
48 
49  auto clkTy = seq::ClockType::get(result.getContext());
50  auto i1 = IntegerType::get(result.getContext(), 1);
51  if (parser.resolveOperands(operands, {clkTy, i1, outputType},
52  inputOperandsLoc, result.operands))
53  return failure();
54  return success();
55 }
56 
57 void ChannelBufferOp::print(OpAsmPrinter &p) {
58  p << " " << getClk() << ", " << getRst() << ", " << getInput();
59  p.printOptionalAttrDict((*this)->getAttrs());
60  p << " : " << innerType();
61 }
62 
63 circt::esi::ChannelType ChannelBufferOp::channelType() {
64  return cast<circt::esi::ChannelType>(getInput().getType());
65 }
66 
67 //===----------------------------------------------------------------------===//
68 // PipelineStageOp functions.
69 //===----------------------------------------------------------------------===//
70 
71 ParseResult PipelineStageOp::parse(OpAsmParser &parser,
72  OperationState &result) {
73  llvm::SMLoc inputOperandsLoc = parser.getCurrentLocation();
74 
75  SmallVector<OpAsmParser::UnresolvedOperand, 4> operands;
76  Type innerOutputType;
77  if (parser.parseOperandList(operands, /*requiredOperandCount=*/3) ||
78  parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
79  parser.parseType(innerOutputType))
80  return failure();
81  auto type =
82  ChannelType::get(parser.getBuilder().getContext(), innerOutputType);
83  result.addTypes({type});
84 
85  auto clkTy = seq::ClockType::get(result.getContext());
86  auto i1 = IntegerType::get(result.getContext(), 1);
87  if (parser.resolveOperands(operands, {clkTy, i1, type}, inputOperandsLoc,
88  result.operands))
89  return failure();
90  return success();
91 }
92 
93 void PipelineStageOp::print(OpAsmPrinter &p) {
94  p << " " << getClk() << ", " << getRst() << ", " << getInput();
95  p.printOptionalAttrDict((*this)->getAttrs());
96  p << " : " << innerType();
97 }
98 
99 circt::esi::ChannelType PipelineStageOp::channelType() {
100  return cast<circt::esi::ChannelType>(getInput().getType());
101 }
102 
103 //===----------------------------------------------------------------------===//
104 // Wrap / unwrap.
105 //===----------------------------------------------------------------------===//
106 
107 ParseResult WrapValidReadyOp::parse(OpAsmParser &parser,
108  OperationState &result) {
109  llvm::SMLoc inputOperandsLoc = parser.getCurrentLocation();
110 
111  llvm::SmallVector<OpAsmParser::UnresolvedOperand, 2> opList;
112  Type innerOutputType;
113  if (parser.parseOperandList(opList, 2, OpAsmParser::Delimiter::None) ||
114  parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
115  parser.parseType(innerOutputType))
116  return failure();
117 
118  auto boolType = parser.getBuilder().getI1Type();
119  auto outputType =
120  ChannelType::get(parser.getBuilder().getContext(), innerOutputType);
121  result.addTypes({outputType, boolType});
122  if (parser.resolveOperands(opList, {innerOutputType, boolType},
123  inputOperandsLoc, result.operands))
124  return failure();
125  return success();
126 }
127 
128 void WrapValidReadyOp::print(OpAsmPrinter &p) {
129  p << " " << getRawInput() << ", " << getValid();
130  p.printOptionalAttrDict((*this)->getAttrs());
131  p << " : " << innerType();
132 }
133 
134 void WrapValidReadyOp::build(OpBuilder &b, OperationState &state, Value data,
135  Value valid) {
136  build(b, state, ChannelType::get(state.getContext(), data.getType()),
137  b.getI1Type(), data, valid);
138 }
139 
140 LogicalResult WrapValidReadyOp::verify() {
141  if (getChanOutput().getType().getSignaling() != ChannelSignaling::ValidReady)
142  return emitOpError("only supports valid-ready signaling");
143  return success();
144 }
145 
146 ParseResult UnwrapValidReadyOp::parse(OpAsmParser &parser,
147  OperationState &result) {
148  llvm::SMLoc inputOperandsLoc = parser.getCurrentLocation();
149 
150  llvm::SmallVector<OpAsmParser::UnresolvedOperand, 2> opList;
151  Type outputType;
152  if (parser.parseOperandList(opList, 2, OpAsmParser::Delimiter::None) ||
153  parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
154  parser.parseType(outputType))
155  return failure();
156 
157  auto inputType =
158  ChannelType::get(parser.getBuilder().getContext(), outputType);
159 
160  auto boolType = parser.getBuilder().getI1Type();
161 
162  result.addTypes({inputType.getInner(), boolType});
163  if (parser.resolveOperands(opList, {inputType, boolType}, inputOperandsLoc,
164  result.operands))
165  return failure();
166  return success();
167 }
168 
169 void UnwrapValidReadyOp::print(OpAsmPrinter &p) {
170  p << " " << getChanInput() << ", " << getReady();
171  p.printOptionalAttrDict((*this)->getAttrs());
172  p << " : " << getRawOutput().getType();
173 }
174 
175 LogicalResult UnwrapValidReadyOp::verify() {
176  if (getChanInput().getType().getSignaling() != ChannelSignaling::ValidReady)
177  return emitOpError("only supports valid-ready signaling");
178  return success();
179 }
180 
181 circt::esi::ChannelType WrapValidReadyOp::channelType() {
182  return cast<circt::esi::ChannelType>(getChanOutput().getType());
183 }
184 
185 void UnwrapValidReadyOp::build(OpBuilder &b, OperationState &state,
186  Value inChan, Value ready) {
187  auto inChanType = cast<ChannelType>(inChan.getType());
188  build(b, state, inChanType.getInner(), b.getI1Type(), inChan, ready);
189 }
190 
191 circt::esi::ChannelType UnwrapValidReadyOp::channelType() {
192  return cast<circt::esi::ChannelType>(getChanInput().getType());
193 }
194 
195 circt::esi::ChannelType WrapFIFOOp::channelType() {
196  return cast<circt::esi::ChannelType>(getChanOutput().getType());
197 }
198 
199 ParseResult parseWrapFIFOType(OpAsmParser &p, Type &dataType,
200  Type &chanInputType) {
201  auto loc = p.getCurrentLocation();
202  ChannelType chType;
203  if (p.parseType(chType))
204  return failure();
205  if (chType.getSignaling() != ChannelSignaling::FIFO0)
206  return p.emitError(loc, "can only wrap into FIFO type");
207  dataType = chType.getInner();
208  chanInputType = chType;
209  return success();
210 }
211 
212 void printWrapFIFOType(OpAsmPrinter &p, WrapFIFOOp wrap, Type dataType,
213  Type chanType) {
214  p << chanType;
215 }
216 
217 LogicalResult WrapFIFOOp::verify() {
218  if (getChanOutput().getType().getSignaling() != ChannelSignaling::FIFO0)
219  return emitOpError("only supports FIFO signaling");
220  return success();
221 }
222 
223 circt::esi::ChannelType UnwrapFIFOOp::channelType() {
224  return cast<circt::esi::ChannelType>(getChanInput().getType());
225 }
226 
227 LogicalResult UnwrapFIFOOp::verify() {
228  if (getChanInput().getType().getSignaling() != ChannelSignaling::FIFO0)
229  return emitOpError("only supports FIFO signaling");
230  return success();
231 }
232 
233 LogicalResult
234 UnwrapFIFOOp::inferReturnTypes(MLIRContext *context, std::optional<Location>,
235  ValueRange operands, DictionaryAttr,
236  mlir::OpaqueProperties, mlir::RegionRange,
237  SmallVectorImpl<Type> &inferredResulTypes) {
238  inferredResulTypes.push_back(
239  cast<ChannelType>(operands[0].getType()).getInner());
240  inferredResulTypes.push_back(
241  IntegerType::get(context, 1, IntegerType::Signless));
242  return success();
243 }
244 
245 /// If 'iface' looks like an ESI interface, return the inner data type.
246 static Type getEsiDataType(circt::sv::InterfaceOp iface) {
247  using namespace circt::sv;
248  if (!iface.lookupSymbol<InterfaceSignalOp>("valid"))
249  return Type();
250  if (!iface.lookupSymbol<InterfaceSignalOp>("ready"))
251  return Type();
252  auto dataSig = iface.lookupSymbol<InterfaceSignalOp>("data");
253  if (!dataSig)
254  return Type();
255  return dataSig.getType();
256 }
257 
258 /// Verify that the modport type of 'modportArg' points to an interface which
259 /// looks like an ESI interface and the inner data from said interface matches
260 /// the chan type's inner data type.
261 static LogicalResult verifySVInterface(Operation *op,
262  circt::sv::ModportType modportType,
263  ChannelType chanType) {
264  auto modport =
265  SymbolTable::lookupNearestSymbolFrom<circt::sv::InterfaceModportOp>(
266  op, modportType.getModport());
267  if (!modport)
268  return op->emitError("Could not find modport ")
269  << modportType.getModport() << " in symbol table.";
270  auto iface = cast<circt::sv::InterfaceOp>(modport->getParentOp());
271  Type esiDataType = getEsiDataType(iface);
272  if (!esiDataType)
273  return op->emitOpError("Interface is not a valid ESI interface.");
274  if (esiDataType != chanType.getInner())
275  return op->emitOpError("Operation specifies ")
276  << chanType << " but type inside doesn't match interface data type "
277  << esiDataType << ".";
278  return success();
279 }
280 
281 LogicalResult WrapSVInterfaceOp::verify() {
282  auto modportType = cast<circt::sv::ModportType>(getInterfaceSink().getType());
283  auto chanType = cast<ChannelType>(getOutput().getType());
284  return verifySVInterface(*this, modportType, chanType);
285 }
286 
287 circt::esi::ChannelType WrapSVInterfaceOp::channelType() {
288  return cast<circt::esi::ChannelType>(getOutput().getType());
289 }
290 
291 LogicalResult UnwrapSVInterfaceOp::verify() {
292  auto modportType =
293  cast<circt::sv::ModportType>(getInterfaceSource().getType());
294  auto chanType = cast<ChannelType>(getChanInput().getType());
295  return verifySVInterface(*this, modportType, chanType);
296 }
297 
298 circt::esi::ChannelType UnwrapSVInterfaceOp::channelType() {
299  return cast<circt::esi::ChannelType>(getChanInput().getType());
300 }
301 
302 LogicalResult WrapWindow::verify() {
303  hw::UnionType expectedInput = getWindow().getType().getLoweredType();
304  if (expectedInput == getFrame().getType())
305  return success();
306  return emitOpError("Expected input type is ") << expectedInput;
307 }
308 
309 LogicalResult
310 UnwrapWindow::inferReturnTypes(MLIRContext *, std::optional<Location>,
311  ValueRange operands, DictionaryAttr,
312  mlir::OpaqueProperties, mlir::RegionRange,
313  SmallVectorImpl<Type> &inferredReturnTypes) {
314  auto windowType = cast<WindowType>(operands.front().getType());
315  inferredReturnTypes.push_back(windowType.getLoweredType());
316  return success();
317 }
318 
319 /// Determine the input type ('frame') from the return type ('window').
320 static bool parseInferWindowRet(OpAsmParser &p, Type &frame, Type &windowOut) {
321  WindowType window;
322  if (p.parseType(window))
323  return true;
324  windowOut = window;
325  frame = window.getLoweredType();
326  return false;
327 }
328 
329 static void printInferWindowRet(OpAsmPrinter &p, Operation *, Type,
330  Type window) {
331  p << window;
332 }
333 
334 //===----------------------------------------------------------------------===//
335 // Services ops.
336 //===----------------------------------------------------------------------===//
337 
338 /// Get the port declaration op for the specified service decl, port name.
340 getServiceDecl(Operation *op, SymbolTableCollection &symbolTable,
341  hw::InnerRefAttr servicePort) {
342  ModuleOp top = op->getParentOfType<mlir::ModuleOp>();
343  SymbolTable &topSyms = symbolTable.getSymbolTable(top);
344 
345  StringAttr modName = servicePort.getModule();
346  auto serviceDecl = topSyms.lookup<ServiceDeclOpInterface>(modName);
347  if (!serviceDecl)
348  return op->emitOpError("Could not find service declaration ")
349  << servicePort.getModuleRef();
350  return serviceDecl;
351 }
352 
353 /// Get the port info for the specified service decl and port name.
355 getServicePortInfo(Operation *op, SymbolTableCollection &symbolTable,
356  hw::InnerRefAttr servicePort) {
357  auto serviceDecl = getServiceDecl(op, symbolTable, servicePort);
358  if (failed(serviceDecl))
359  return failure();
360  auto portInfo = serviceDecl->getPortInfo(servicePort.getName());
361  if (failed(portInfo))
362  return op->emitOpError("Could not locate port ") << servicePort.getName();
363  return portInfo;
364 }
365 
366 /// Check that the channels on two bundles match allowing for AnyType in the
367 /// 'svc' bundle.
368 static LogicalResult checkTypeMatch(Operation *req,
369  ChannelBundleType svcBundleType,
370  ChannelBundleType reqBundleType,
371  bool skipDirectionCheck) {
372  auto *ctxt = svcBundleType.getContext();
373  auto anyChannelType = ChannelType::get(ctxt, AnyType::get(ctxt));
374 
375  if (svcBundleType.getChannels().size() != reqBundleType.getChannels().size())
376  return req->emitOpError(
377  "Request port bundle channel count does not match service "
378  "port bundle channel count");
379 
380  // Build fast lookup.
381  DenseMap<StringAttr, BundledChannel> declBundleChannels;
382  for (BundledChannel bc : svcBundleType.getChannels())
383  declBundleChannels[bc.name] = bc;
384 
385  // Check all the channels.
386  for (BundledChannel bc : reqBundleType.getChannels()) {
387  auto f = declBundleChannels.find(bc.name);
388  if (f == declBundleChannels.end())
389  return req->emitOpError(
390  "Request channel name not found in service port bundle");
391  if (!skipDirectionCheck && f->second.direction != bc.direction)
392  return req->emitOpError(
393  "Request channel direction does not match service "
394  "port bundle channel direction");
395 
396  if (f->second.type != bc.type && f->second.type != anyChannelType)
397  return req->emitOpError(
398  "Request channel type does not match service port "
399  "bundle channel type");
400  }
401  return success();
402 }
403 
404 LogicalResult
405 RequestConnectionOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
406  auto svcPort = getServicePortInfo(*this, symbolTable, getServicePortAttr());
407  if (failed(svcPort))
408  return failure();
409  return checkTypeMatch(*this, svcPort->type, getToClient().getType(), false);
410 }
411 
412 LogicalResult ServiceImplementConnReqOp::verifySymbolUses(
413  SymbolTableCollection &symbolTable) {
414  auto svcPort = getServicePortInfo(*this, symbolTable, getServicePortAttr());
415  if (failed(svcPort))
416  return failure();
417  return checkTypeMatch(*this, svcPort->type, getToClient().getType(), true);
418 }
419 
420 void CustomServiceDeclOp::getPortList(SmallVectorImpl<ServicePortInfo> &ports) {
421  for (auto toClient : getOps<ServiceDeclPortOp>())
422  ports.push_back(ServicePortInfo{
423  hw::InnerRefAttr::get(getSymNameAttr(), toClient.getInnerSymAttr()),
424  toClient.getToClientType()});
425 }
426 
427 //===----------------------------------------------------------------------===//
428 // Bundle ops.
429 //===----------------------------------------------------------------------===//
430 
431 static ParseResult
432 parseUnPackBundleType(OpAsmParser &parser,
433  SmallVectorImpl<Type> &toChannelTypes,
434  SmallVectorImpl<Type> &fromChannelTypes, Type &type) {
435 
436  ChannelBundleType bundleType;
437  if (parser.parseType(bundleType))
438  return failure();
439  type = bundleType;
440 
441  for (BundledChannel ch : bundleType.getChannels())
442  if (ch.direction == ChannelDirection::to)
443  toChannelTypes.push_back(ch.type);
444  else if (ch.direction == ChannelDirection::from)
445  fromChannelTypes.push_back(ch.type);
446  else
447  assert(false && "Channel direction invalid");
448  return success();
449 }
450 template <typename T3, typename T4>
451 static void printUnPackBundleType(OpAsmPrinter &p, Operation *, T3, T4,
452  Type bundleType) {
453  p.printType(bundleType);
454 }
455 void UnpackBundleOp::build(::mlir::OpBuilder &odsBuilder,
456  ::mlir::OperationState &odsState, Value bundle,
457  mlir::ValueRange fromChannels) {
458  for (BundledChannel ch :
459  cast<ChannelBundleType>(bundle.getType()).getChannels())
460  if (ch.direction == ChannelDirection::to)
461  odsState.addTypes(ch.type);
462  odsState.addOperands(bundle);
463  odsState.addOperands(fromChannels);
464 }
465 
466 LogicalResult PackBundleOp::verify() {
467  if (!getBundle().hasOneUse())
468  return emitOpError("bundles must have exactly one user");
469  return success();
470 }
471 void PackBundleOp::build(::mlir::OpBuilder &odsBuilder,
472  ::mlir::OperationState &odsState,
473  ChannelBundleType bundleType,
474  mlir::ValueRange toChannels) {
475  odsState.addTypes(bundleType);
476  for (BundledChannel ch : cast<ChannelBundleType>(bundleType).getChannels())
477  if (ch.direction == ChannelDirection::from)
478  odsState.addTypes(ch.type);
479  odsState.addOperands(toChannels);
480 }
481 
482 LogicalResult UnpackBundleOp::verify() {
483  if (!getBundle().hasOneUse())
484  return emitOpError("bundles must have exactly one user");
485  return success();
486 }
487 
489  if (getNumResults() == 0)
490  return;
491  setNameFn(getResult(0), "bundle");
492  for (auto [idx, from] : llvm::enumerate(llvm::make_filter_range(
493  getBundle().getType().getChannels(), [](BundledChannel ch) {
494  return ch.direction == ChannelDirection::from;
495  })))
496  if (idx + 1 < getNumResults())
497  setNameFn(getResult(idx + 1), from.name.getValue());
498 }
499 
501  for (auto [idx, to] : llvm::enumerate(llvm::make_filter_range(
502  getBundle().getType().getChannels(), [](BundledChannel ch) {
503  return ch.direction == ChannelDirection::to;
504  })))
505  if (idx < getNumResults())
506  setNameFn(getResult(idx), to.name.getValue());
507 }
508 //===----------------------------------------------------------------------===//
509 // Structural ops.
510 //===----------------------------------------------------------------------===//
511 
512 LogicalResult ESIPureModuleOp::verify() {
513  ESIDialect *esiDialect = getContext()->getLoadedDialect<ESIDialect>();
514  Block &body = getBody().front();
515  auto channelOrOutput = [](Value v) {
516  if (isa<ChannelType, ChannelBundleType>(v.getType()))
517  return true;
518  if (v.getUsers().empty())
519  return false;
520  return llvm::all_of(v.getUsers(), [](Operation *op) {
521  return isa<ESIPureModuleOutputOp>(op);
522  });
523  };
524 
525  DenseMap<StringAttr, std::tuple<hw::ModulePort::Direction, Type, Operation *>>
526  ports;
527  for (Operation &op : body.getOperations()) {
528  if (igraph::InstanceOpInterface inst =
529  dyn_cast<igraph::InstanceOpInterface>(op)) {
530  if (llvm::any_of(op.getOperands(), [](Value v) {
531  return !(isa<ChannelType, ChannelBundleType>(v.getType()) ||
532  isa<ESIPureModuleInputOp>(v.getDefiningOp()));
533  }))
534  return inst.emitOpError(
535  "instances in ESI pure modules can only contain channel ports or "
536  "ports driven by 'input' ops");
537  if (!llvm::all_of(op.getResults(), channelOrOutput))
538  return inst.emitOpError(
539  "instances in ESI pure modules can only contain channel ports or "
540  "drive only 'outputs'");
541  } else {
542  // Pure modules can only contain instance ops and ESI ops.
543  if (op.getDialect() != esiDialect)
544  return op.emitOpError("operation not allowed in ESI pure modules");
545  }
546 
547  // Check for port validity.
548  if (auto port = dyn_cast<ESIPureModuleInputOp>(op)) {
549  auto existing = ports.find(port.getNameAttr());
550  Type portType = port.getResult().getType();
551  if (existing != ports.end()) {
552  auto [dir, type, op] = existing->getSecond();
553  if (dir != hw::ModulePort::Direction::Input || type != portType)
554  return (port.emitOpError("port '")
555  << port.getName() << "' previously declared as type " << type)
556  .attachNote(op->getLoc());
557  }
558  ports[port.getNameAttr()] = std::make_tuple(
559  hw::ModulePort::Direction::Input, portType, port.getOperation());
560  } else if (auto port = dyn_cast<ESIPureModuleOutputOp>(op)) {
561  auto existing = ports.find(port.getNameAttr());
562  if (existing != ports.end())
563  return (port.emitOpError("port '")
564  << port.getName() << "' previously declared")
565  .attachNote(std::get<2>(existing->getSecond())->getLoc());
566  ports[port.getNameAttr()] =
567  std::make_tuple(hw::ModulePort::Direction::Input,
568  port.getValue().getType(), port.getOperation());
569  }
570  }
571  return success();
572 }
573 
574 hw::ModuleType ESIPureModuleOp::getHWModuleType() {
575  return hw::ModuleType::get(getContext(), {});
576 }
577 
578 SmallVector<::circt::hw::PortInfo> ESIPureModuleOp::getPortList() { return {}; }
580  ::llvm::report_fatal_error("not supported");
581 }
582 
583 size_t ESIPureModuleOp::getNumPorts() { return 0; }
584 size_t ESIPureModuleOp::getNumInputPorts() { return 0; }
585 size_t ESIPureModuleOp::getNumOutputPorts() { return 0; }
586 size_t ESIPureModuleOp::getPortIdForInputId(size_t) {
587  assert(0 && "Out of bounds input port id");
588  return ~0ULL;
589 }
590 size_t ESIPureModuleOp::getPortIdForOutputId(size_t) {
591  assert(0 && "Out of bounds output port id");
592  return ~0ULL;
593 }
594 
595 SmallVector<Location> ESIPureModuleOp::getAllPortLocs() {
596  SmallVector<Location> retval;
597  return retval;
598 }
599 
600 void ESIPureModuleOp::setAllPortLocsAttrs(ArrayRef<Attribute> locs) {
601  emitError("No ports for port locations");
602 }
603 
604 void ESIPureModuleOp::setAllPortNames(ArrayRef<Attribute> names) {
605  emitError("No ports for port naming");
606 }
607 
608 void ESIPureModuleOp::setAllPortAttrs(ArrayRef<Attribute> attrs) {
609  emitError("No ports for port attributes");
610 }
611 
612 void ESIPureModuleOp::removeAllPortAttrs() {
613  emitError("No ports for port attributes)");
614 }
615 
616 ArrayRef<Attribute> ESIPureModuleOp::getAllPortAttrs() { return {}; }
617 
618 void ESIPureModuleOp::setHWModuleType(hw::ModuleType type) {
619  emitError("No ports for port types");
620 }
621 
622 //===----------------------------------------------------------------------===//
623 // Manifest ops.
624 //===----------------------------------------------------------------------===//
625 
626 StringRef ServiceImplRecordOp::getManifestClass() { return "service"; }
627 
628 void ServiceImplRecordOp::getDetails(SmallVectorImpl<NamedAttribute> &results) {
629  auto *ctxt = getContext();
630  // AppID, optionally the service name, implementation name and details.
631  results.emplace_back(getAppIDAttrName(), getAppIDAttr());
632  if (getService())
633  results.emplace_back(getServiceAttrName(), getServiceAttr());
634  results.emplace_back(getServiceImplNameAttrName(), getServiceImplNameAttr());
635  // Don't add another level for the implementation details.
636  for (auto implDetail : getImplDetailsAttr().getValue())
637  results.push_back(implDetail);
638 
639  // All of the manifest data contained by this op.
640  SmallVector<Attribute, 8> reqDetails;
641  for (auto reqDetail : getReqDetails().front().getOps<IsManifestData>())
642  reqDetails.push_back(reqDetail.getDetailsAsDict());
643  results.emplace_back(StringAttr::get(ctxt, "client_details"),
644  ArrayAttr::get(ctxt, reqDetails));
645 }
646 
647 bool parseServiceImplRecordReqDetails(OpAsmParser &parser,
648  Region &reqDetailsRegion) {
649  parser.parseOptionalRegion(reqDetailsRegion);
650  if (reqDetailsRegion.empty())
651  reqDetailsRegion.emplaceBlock();
652  return false;
653 }
654 
655 void printServiceImplRecordReqDetails(OpAsmPrinter &p, ServiceImplRecordOp,
656  Region &reqDetailsRegion) {
657  if (!reqDetailsRegion.empty() && !reqDetailsRegion.front().empty())
658  p.printRegion(reqDetailsRegion, /*printEntryBlockArgs=*/false,
659  /*printBlockTerminators=*/false);
660 }
661 
662 StringRef ServiceImplClientRecordOp::getManifestClass() {
663  return "service_client";
664 }
665 void ServiceImplClientRecordOp::getDetails(
666  SmallVectorImpl<NamedAttribute> &results) {
667  // Relative AppID path, service port, and implementation details. Don't add
668  // the bundle type since it is meaningless to the host and just clutters the
669  // output.
670  results.emplace_back(getRelAppIDPathAttrName(), getRelAppIDPathAttr());
671  results.emplace_back(getServicePortAttrName(), getServicePortAttr());
672  // Don't add another level for the implementation details.
673  for (auto implDetail : getImplDetailsAttr().getValue())
674  results.push_back(implDetail);
675 }
676 
677 StringRef ServiceRequestRecordOp::getManifestClass() { return "client_port"; }
678 
679 void ServiceRequestRecordOp::getDetails(
680  SmallVectorImpl<NamedAttribute> &results) {
681  auto *ctxt = getContext();
682  results.emplace_back(StringAttr::get(ctxt, "appID"), getRequestorAttr());
683  results.emplace_back(getBundleTypeAttrName(), getBundleTypeAttr());
684  results.emplace_back(getServicePortAttrName(), getServicePortAttr());
685  if (auto stdSvc = getStdServiceAttr())
686  results.emplace_back(getStdServiceAttrName(), getStdServiceAttr());
687 }
688 
689 StringRef SymbolMetadataOp::getManifestClass() { return "sym_info"; }
690 
691 #define GET_OP_CLASSES
692 #include "circt/Dialect/ESI/ESI.cpp.inc"
693 
694 #include "circt/Dialect/ESI/ESIInterfaces.cpp.inc"
assert(baseType &&"element must be base type")
return wrap(CMemoryType::get(unwrap(ctx), baseType, numElements))
static bool parseInferWindowRet(OpAsmParser &p, Type &frame, Type &windowOut)
Determine the input type ('frame') from the return type ('window').
Definition: ESIOps.cpp:320
ParseResult parseWrapFIFOType(OpAsmParser &p, Type &dataType, Type &chanInputType)
Definition: ESIOps.cpp:199
static LogicalResult verifySVInterface(Operation *op, circt::sv::ModportType modportType, ChannelType chanType)
Verify that the modport type of 'modportArg' points to an interface which looks like an ESI interface...
Definition: ESIOps.cpp:261
static LogicalResult checkTypeMatch(Operation *req, ChannelBundleType svcBundleType, ChannelBundleType reqBundleType, bool skipDirectionCheck)
Check that the channels on two bundles match allowing for AnyType in the 'svc' bundle.
Definition: ESIOps.cpp:368
void printServiceImplRecordReqDetails(OpAsmPrinter &p, ServiceImplRecordOp, Region &reqDetailsRegion)
Definition: ESIOps.cpp:655
static FailureOr< ServicePortInfo > getServicePortInfo(Operation *op, SymbolTableCollection &symbolTable, hw::InnerRefAttr servicePort)
Get the port info for the specified service decl and port name.
Definition: ESIOps.cpp:355
static Type getEsiDataType(circt::sv::InterfaceOp iface)
If 'iface' looks like an ESI interface, return the inner data type.
Definition: ESIOps.cpp:246
static FailureOr< ServiceDeclOpInterface > getServiceDecl(Operation *op, SymbolTableCollection &symbolTable, hw::InnerRefAttr servicePort)
Get the port declaration op for the specified service decl, port name.
Definition: ESIOps.cpp:340
static void printUnPackBundleType(OpAsmPrinter &p, Operation *, T3, T4, Type bundleType)
Definition: ESIOps.cpp:451
static ParseResult parseUnPackBundleType(OpAsmParser &parser, SmallVectorImpl< Type > &toChannelTypes, SmallVectorImpl< Type > &fromChannelTypes, Type &type)
Definition: ESIOps.cpp:432
static void printInferWindowRet(OpAsmPrinter &p, Operation *, Type, Type window)
Definition: ESIOps.cpp:329
void printWrapFIFOType(OpAsmPrinter &p, WrapFIFOOp wrap, Type dataType, Type chanType)
Definition: ESIOps.cpp:212
bool parseServiceImplRecordReqDetails(OpAsmParser &parser, Region &reqDetailsRegion)
Definition: ESIOps.cpp:647
static SmallVector< Location > getAllPortLocs(ModTy module)
Definition: HWOps.cpp:1192
static void setAllPortNames(ArrayRef< Attribute > names, ModTy module)
Definition: HWOps.cpp:1262
static void setHWModuleType(ModTy &mod, ModuleType type)
Definition: HWOps.cpp:1335
static SmallVector< PortInfo > getPortList(ModuleTy &mod)
Definition: HWOps.cpp:1414
static PortInfo getPort(ModuleTy &mod, size_t idx)
Definition: HWOps.cpp:1434
@ Input
Definition: HW.h:35
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:54
mlir::Type innerType(mlir::Type type)
Definition: ESITypes.cpp:184
LogicalResult inferReturnTypes(MLIRContext *context, std::optional< Location > loc, ValueRange operands, DictionaryAttr attrs, mlir::OpaqueProperties properties, mlir::RegionRange regions, SmallVectorImpl< Type > &results, llvm::function_ref< FIRRTLType(ValueRange, ArrayRef< NamedAttribute >, std::optional< Location >)> callback)
size_t getNumPorts(Operation *op)
Return the number of ports in a module-like thing (modules, memories, etc)
Definition: FIRRTLOps.cpp:271
void getAsmResultNames(OpAsmSetValueNameFn setNameFn, StringRef instanceName, ArrayAttr resultNames, ValueRange results)
Suggest a name for each result value based on the saved result names attribute.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
function_ref< void(Value, StringRef)> OpAsmSetValueNameFn
Definition: LLVM.h:186
ChannelDirection direction
Definition: ESITypes.h:38
Describes a service port.
Definition: ESIOps.h:31
This holds the name, type, direction of a module's ports.