CIRCT  20.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 LogicalResult ChannelBufferOp::verify() {
68  if (getInput().getType().getSignaling() != ChannelSignaling::ValidReady)
69  return emitOpError("currently only supports valid-ready signaling");
70  if (getOutput().getType().getDataDelay() != 0)
71  return emitOpError("currently only supports channels with zero data delay");
72  return success();
73 }
74 
75 //===----------------------------------------------------------------------===//
76 // FIFO functions.
77 //===----------------------------------------------------------------------===//
78 
79 LogicalResult FIFOOp::verify() {
80  if (getOutput().getType().getInner() != getInput().getType().getInner())
81  return emitOpError("input and output types must match");
82  return success();
83 }
84 
85 //===----------------------------------------------------------------------===//
86 // PipelineStageOp functions.
87 //===----------------------------------------------------------------------===//
88 
89 ParseResult PipelineStageOp::parse(OpAsmParser &parser,
90  OperationState &result) {
91  llvm::SMLoc inputOperandsLoc = parser.getCurrentLocation();
92 
93  SmallVector<OpAsmParser::UnresolvedOperand, 4> operands;
94  Type innerOutputType;
95  if (parser.parseOperandList(operands, /*requiredOperandCount=*/3) ||
96  parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
97  parser.parseType(innerOutputType))
98  return failure();
99  auto type =
100  ChannelType::get(parser.getBuilder().getContext(), innerOutputType);
101  result.addTypes({type});
102 
103  auto clkTy = seq::ClockType::get(result.getContext());
104  auto i1 = IntegerType::get(result.getContext(), 1);
105  if (parser.resolveOperands(operands, {clkTy, i1, type}, inputOperandsLoc,
106  result.operands))
107  return failure();
108  return success();
109 }
110 
111 void PipelineStageOp::print(OpAsmPrinter &p) {
112  p << " " << getClk() << ", " << getRst() << ", " << getInput();
113  p.printOptionalAttrDict((*this)->getAttrs());
114  p << " : " << innerType();
115 }
116 
117 circt::esi::ChannelType PipelineStageOp::channelType() {
118  return cast<circt::esi::ChannelType>(getInput().getType());
119 }
120 
121 //===----------------------------------------------------------------------===//
122 // Wrap / unwrap.
123 //===----------------------------------------------------------------------===//
124 
125 ParseResult WrapValidReadyOp::parse(OpAsmParser &parser,
126  OperationState &result) {
127  llvm::SMLoc inputOperandsLoc = parser.getCurrentLocation();
128 
129  llvm::SmallVector<OpAsmParser::UnresolvedOperand, 2> opList;
130  Type innerOutputType;
131  if (parser.parseOperandList(opList, 2, OpAsmParser::Delimiter::None) ||
132  parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
133  parser.parseType(innerOutputType))
134  return failure();
135 
136  auto boolType = parser.getBuilder().getI1Type();
137  auto outputType =
138  ChannelType::get(parser.getBuilder().getContext(), innerOutputType);
139  result.addTypes({outputType, boolType});
140  if (parser.resolveOperands(opList, {innerOutputType, boolType},
141  inputOperandsLoc, result.operands))
142  return failure();
143  return success();
144 }
145 
146 void WrapValidReadyOp::print(OpAsmPrinter &p) {
147  p << " " << getRawInput() << ", " << getValid();
148  p.printOptionalAttrDict((*this)->getAttrs());
149  p << " : " << innerType();
150 }
151 
152 void WrapValidReadyOp::build(OpBuilder &b, OperationState &state, Value data,
153  Value valid) {
154  build(b, state, ChannelType::get(state.getContext(), data.getType()),
155  b.getI1Type(), data, valid);
156 }
157 
158 LogicalResult WrapValidReadyOp::verify() {
159  mlir::TypedValue<ChannelType> chanOut = getChanOutput();
160  if (chanOut.getType().getSignaling() != ChannelSignaling::ValidReady)
161  return emitOpError("only supports valid-ready signaling");
162  if (!chanOut.hasOneUse() && !chanOut.getUses().empty()) {
163  llvm::errs() << "chanOut: " << chanOut.getLoc() << "\n";
164  return emitOpError("only supports zero or one use");
165  }
166  return success();
167 }
168 
169 ParseResult UnwrapValidReadyOp::parse(OpAsmParser &parser,
170  OperationState &result) {
171  llvm::SMLoc inputOperandsLoc = parser.getCurrentLocation();
172 
173  llvm::SmallVector<OpAsmParser::UnresolvedOperand, 2> opList;
174  Type outputType;
175  if (parser.parseOperandList(opList, 2, OpAsmParser::Delimiter::None) ||
176  parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
177  parser.parseType(outputType))
178  return failure();
179 
180  auto inputType =
181  ChannelType::get(parser.getBuilder().getContext(), outputType);
182 
183  auto boolType = parser.getBuilder().getI1Type();
184 
185  result.addTypes({inputType.getInner(), boolType});
186  if (parser.resolveOperands(opList, {inputType, boolType}, inputOperandsLoc,
187  result.operands))
188  return failure();
189  return success();
190 }
191 
192 void UnwrapValidReadyOp::print(OpAsmPrinter &p) {
193  p << " " << getChanInput() << ", " << getReady();
194  p.printOptionalAttrDict((*this)->getAttrs());
195  p << " : " << getRawOutput().getType();
196 }
197 
198 LogicalResult UnwrapValidReadyOp::verify() {
199  if (getChanInput().getType().getSignaling() != ChannelSignaling::ValidReady)
200  return emitOpError("only supports valid-ready signaling");
201  return success();
202 }
203 
204 circt::esi::ChannelType WrapValidReadyOp::channelType() {
205  return cast<circt::esi::ChannelType>(getChanOutput().getType());
206 }
207 
208 void UnwrapValidReadyOp::build(OpBuilder &b, OperationState &state,
209  Value inChan, Value ready) {
210  auto inChanType = cast<ChannelType>(inChan.getType());
211  build(b, state, inChanType.getInner(), b.getI1Type(), inChan, ready);
212 }
213 
214 circt::esi::ChannelType UnwrapValidReadyOp::channelType() {
215  return cast<circt::esi::ChannelType>(getChanInput().getType());
216 }
217 
218 circt::esi::ChannelType WrapFIFOOp::channelType() {
219  return cast<circt::esi::ChannelType>(getChanOutput().getType());
220 }
221 
222 ParseResult parseWrapFIFOType(OpAsmParser &p, Type &dataType,
223  Type &chanInputType) {
224  auto loc = p.getCurrentLocation();
225  ChannelType chType;
226  if (p.parseType(chType))
227  return failure();
228  if (chType.getSignaling() != ChannelSignaling::FIFO)
229  return p.emitError(loc, "can only wrap into FIFO type");
230  dataType = chType.getInner();
231  chanInputType = chType;
232  return success();
233 }
234 
235 void printWrapFIFOType(OpAsmPrinter &p, WrapFIFOOp wrap, Type dataType,
236  Type chanType) {
237  p << chanType;
238 }
239 
240 LogicalResult WrapFIFOOp::verify() {
241  if (getChanOutput().getType().getSignaling() != ChannelSignaling::FIFO)
242  return emitOpError("only supports FIFO signaling");
243  return success();
244 }
245 
246 circt::esi::ChannelType UnwrapFIFOOp::channelType() {
247  return cast<circt::esi::ChannelType>(getChanInput().getType());
248 }
249 
250 LogicalResult UnwrapFIFOOp::verify() {
251  if (getChanInput().getType().getSignaling() != ChannelSignaling::FIFO)
252  return emitOpError("only supports FIFO signaling");
253  return success();
254 }
255 
256 LogicalResult
257 UnwrapFIFOOp::inferReturnTypes(MLIRContext *context, std::optional<Location>,
258  ValueRange operands, DictionaryAttr,
259  mlir::OpaqueProperties, mlir::RegionRange,
260  SmallVectorImpl<Type> &inferredResulTypes) {
261  inferredResulTypes.push_back(
262  cast<ChannelType>(operands[0].getType()).getInner());
263  inferredResulTypes.push_back(
264  IntegerType::get(context, 1, IntegerType::Signless));
265  return success();
266 }
267 
268 /// If 'iface' looks like an ESI interface, return the inner data type.
269 static Type getEsiDataType(circt::sv::InterfaceOp iface) {
270  using namespace circt::sv;
271  if (!iface.lookupSymbol<InterfaceSignalOp>("valid"))
272  return Type();
273  if (!iface.lookupSymbol<InterfaceSignalOp>("ready"))
274  return Type();
275  auto dataSig = iface.lookupSymbol<InterfaceSignalOp>("data");
276  if (!dataSig)
277  return Type();
278  return dataSig.getType();
279 }
280 
281 /// Verify that the modport type of 'modportArg' points to an interface which
282 /// looks like an ESI interface and the inner data from said interface matches
283 /// the chan type's inner data type.
284 static LogicalResult verifySVInterface(Operation *op,
285  circt::sv::ModportType modportType,
286  ChannelType chanType) {
287  auto modport =
288  SymbolTable::lookupNearestSymbolFrom<circt::sv::InterfaceModportOp>(
289  op, modportType.getModport());
290  if (!modport)
291  return op->emitError("Could not find modport ")
292  << modportType.getModport() << " in symbol table.";
293  auto iface = cast<circt::sv::InterfaceOp>(modport->getParentOp());
294  Type esiDataType = getEsiDataType(iface);
295  if (!esiDataType)
296  return op->emitOpError("Interface is not a valid ESI interface.");
297  if (esiDataType != chanType.getInner())
298  return op->emitOpError("Operation specifies ")
299  << chanType << " but type inside doesn't match interface data type "
300  << esiDataType << ".";
301  return success();
302 }
303 
304 LogicalResult WrapSVInterfaceOp::verify() {
305  auto modportType = cast<circt::sv::ModportType>(getInterfaceSink().getType());
306  auto chanType = cast<ChannelType>(getOutput().getType());
307  return verifySVInterface(*this, modportType, chanType);
308 }
309 
310 circt::esi::ChannelType WrapSVInterfaceOp::channelType() {
311  return cast<circt::esi::ChannelType>(getOutput().getType());
312 }
313 
314 LogicalResult UnwrapSVInterfaceOp::verify() {
315  auto modportType =
316  cast<circt::sv::ModportType>(getInterfaceSource().getType());
317  auto chanType = cast<ChannelType>(getChanInput().getType());
318  return verifySVInterface(*this, modportType, chanType);
319 }
320 
321 circt::esi::ChannelType UnwrapSVInterfaceOp::channelType() {
322  return cast<circt::esi::ChannelType>(getChanInput().getType());
323 }
324 
325 LogicalResult WrapWindow::verify() {
326  hw::UnionType expectedInput = getWindow().getType().getLoweredType();
327  if (expectedInput == getFrame().getType())
328  return success();
329  return emitOpError("Expected input type is ") << expectedInput;
330 }
331 
332 LogicalResult
333 UnwrapWindow::inferReturnTypes(MLIRContext *, std::optional<Location>,
334  ValueRange operands, DictionaryAttr,
335  mlir::OpaqueProperties, mlir::RegionRange,
336  SmallVectorImpl<Type> &inferredReturnTypes) {
337  auto windowType = cast<WindowType>(operands.front().getType());
338  inferredReturnTypes.push_back(windowType.getLoweredType());
339  return success();
340 }
341 
342 /// Determine the input type ('frame') from the return type ('window').
343 static bool parseInferWindowRet(OpAsmParser &p, Type &frame, Type &windowOut) {
344  WindowType window;
345  if (p.parseType(window))
346  return true;
347  windowOut = window;
348  frame = window.getLoweredType();
349  return false;
350 }
351 
352 static void printInferWindowRet(OpAsmPrinter &p, Operation *, Type,
353  Type window) {
354  p << window;
355 }
356 
357 //===----------------------------------------------------------------------===//
358 // Services ops.
359 //===----------------------------------------------------------------------===//
360 
361 /// Get the port declaration op for the specified service decl, port name.
362 static FailureOr<ServiceDeclOpInterface>
363 getServiceDecl(Operation *op, SymbolTableCollection &symbolTable,
364  hw::InnerRefAttr servicePort) {
365  ModuleOp top = op->getParentOfType<mlir::ModuleOp>();
366  SymbolTable &topSyms = symbolTable.getSymbolTable(top);
367 
368  StringAttr modName = servicePort.getModule();
369  auto serviceDecl = topSyms.lookup<ServiceDeclOpInterface>(modName);
370  if (!serviceDecl)
371  return op->emitOpError("Could not find service declaration ")
372  << servicePort.getModuleRef();
373  return serviceDecl;
374 }
375 
376 /// Get the port info for the specified service decl and port name.
377 static FailureOr<ServicePortInfo>
378 getServicePortInfo(Operation *op, SymbolTableCollection &symbolTable,
379  hw::InnerRefAttr servicePort) {
380  auto serviceDecl = getServiceDecl(op, symbolTable, servicePort);
381  if (failed(serviceDecl))
382  return failure();
383  auto portInfo = serviceDecl->getPortInfo(servicePort.getName());
384  if (failed(portInfo))
385  return op->emitOpError("Could not locate port ") << servicePort.getName();
386  return portInfo;
387 }
388 
389 namespace circt {
390 namespace esi {
391 // Check that the channels on two bundles match allowing for AnyType.
392 // NOLINTNEXTLINE(misc-no-recursion)
393 LogicalResult checkInnerTypeMatch(Type expected, Type actual) {
394  if (expected == actual)
395  return success();
396 
397  // Check all of the 'container' or special case types.
398  return TypeSwitch<Type, LogicalResult>(expected)
399  // For 'any' types, we can match anything.
400  .Case<AnyType>([&](Type) { return success(); })
401  // If they're both channels, check the inner types.
402  .Case<ChannelType>([&](ChannelType expectedChannel) {
403  auto actualChannel = dyn_cast<ChannelType>(actual);
404  if (!actualChannel)
405  return failure();
406  return checkInnerTypeMatch(expectedChannel.getInner(),
407  actualChannel.getInner());
408  })
409  // For structs, check each field.
410  .Case<hw::StructType>([&](hw::StructType expectedStruct) {
411  auto actualStruct = dyn_cast<hw::StructType>(actual);
412  if (!actualStruct)
413  return failure();
414  auto expectedFields = expectedStruct.getElements();
415  auto actualFields = actualStruct.getElements();
416  if (expectedFields.size() != actualFields.size())
417  return failure();
418  for (auto [efield, afield] : llvm::zip(expectedFields, actualFields)) {
419  if (efield.name != afield.name)
420  return failure();
421  if (failed(checkInnerTypeMatch(efield.type, afield.type)))
422  return failure();
423  }
424  return success();
425  })
426  // For arrays, check the element type and size.
427  .Case<hw::ArrayType>([&](hw::ArrayType expectedArray) {
428  auto actualArray = dyn_cast<hw::ArrayType>(actual);
429  if (!actualArray)
430  return failure();
431  if (expectedArray.getNumElements() != actualArray.getNumElements())
432  return failure();
433  return checkInnerTypeMatch(expectedArray.getElementType(),
434  actualArray.getElementType());
435  })
436  // For unions, check the element types and names.
437  .Case<hw::UnionType>([&](hw::UnionType expectedUnion) {
438  auto actualUnion = dyn_cast<hw::UnionType>(actual);
439  if (!actualUnion)
440  return failure();
441  auto expectedElements = expectedUnion.getElements();
442  auto actualElements = actualUnion.getElements();
443  if (expectedElements.size() != actualElements.size())
444  return failure();
445  for (auto [efield, afield] :
446  llvm::zip(expectedElements, actualElements)) {
447  if (efield.name != afield.name)
448  return failure();
449  if (efield.offset != afield.offset)
450  return failure();
451  if (failed(checkInnerTypeMatch(efield.type, afield.type)))
452  return failure();
453  }
454  return success();
455  })
456  // For ESI lists, check the element type.
457  .Case<ListType>([&](ListType expectedList) {
458  auto actualList = dyn_cast<ListType>(actual);
459  if (!actualList)
460  return failure();
461  return checkInnerTypeMatch(expectedList.getElementType(),
462  actualList.getElementType());
463  })
464  // For ESI windows, unwrap and check the inner type.
465  .Case<WindowType>([&](WindowType expectedWindow) {
466  auto actualWindow = dyn_cast<WindowType>(actual);
467  if (!actualWindow)
468  return checkInnerTypeMatch(expectedWindow.getInto(), actual);
469  return checkInnerTypeMatch(expectedWindow.getInto(),
470  actualWindow.getInto());
471  })
472  // For type aliases, unwrap and check the aliased type.
473  .Case<hw::TypeAliasType>([&](hw::TypeAliasType expectedAlias) {
474  auto actualAlias = dyn_cast<hw::TypeAliasType>(actual);
475  if (!actualAlias)
476  return checkInnerTypeMatch(expectedAlias.getCanonicalType(), actual);
477  return checkInnerTypeMatch(expectedAlias.getCanonicalType(),
478  actualAlias.getCanonicalType());
479  })
480  // TODO: other container types.
481  .Default([&](Type) { return failure(); });
482 }
483 
484 /// Check that the channels on two bundles match allowing for AnyType in the
485 /// 'svc' bundle.
486 LogicalResult checkBundleTypeMatch(Operation *req,
487  ChannelBundleType svcBundleType,
488  ChannelBundleType reqBundleType,
489  bool skipDirectionCheck) {
490  if (svcBundleType.getChannels().size() != reqBundleType.getChannels().size())
491  return req->emitOpError(
492  "Request port bundle channel count does not match service "
493  "port bundle channel count");
494 
495  // Build fast lookup.
496  DenseMap<StringAttr, BundledChannel> declBundleChannels;
497  for (BundledChannel bc : svcBundleType.getChannels())
498  declBundleChannels[bc.name] = bc;
499 
500  // Check all the channels.
501  for (BundledChannel bc : reqBundleType.getChannels()) {
502  auto f = declBundleChannels.find(bc.name);
503  if (f == declBundleChannels.end())
504  return req->emitOpError(
505  "Request channel name not found in service port bundle");
506  if (!skipDirectionCheck && f->second.direction != bc.direction)
507  return req->emitOpError(
508  "Request channel direction does not match service "
509  "port bundle channel direction");
510 
511  if (failed(checkInnerTypeMatch(f->second.type, bc.type)))
512  return req->emitOpError(
513  "Request channel type does not match service port "
514  "bundle channel type")
515  .attachNote()
516  << "Service port '" << bc.name.getValue()
517  << "' type: " << f->second.type;
518  }
519  return success();
520 }
521 
522 } // namespace esi
523 } // namespace circt
524 
525 LogicalResult
526 RequestConnectionOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
527  auto svcPort = getServicePortInfo(*this, symbolTable, getServicePortAttr());
528  if (failed(svcPort))
529  return failure();
530  return checkBundleTypeMatch(*this, svcPort->type, getToClient().getType(),
531  false);
532 }
533 
534 LogicalResult ServiceImplementConnReqOp::verifySymbolUses(
535  SymbolTableCollection &symbolTable) {
536  auto svcPort = getServicePortInfo(*this, symbolTable, getServicePortAttr());
537  if (failed(svcPort))
538  return failure();
539  return checkBundleTypeMatch(*this, svcPort->type, getToClient().getType(),
540  true);
541 }
542 
543 void CustomServiceDeclOp::getPortList(SmallVectorImpl<ServicePortInfo> &ports) {
544  for (auto toClient : getOps<ServiceDeclPortOp>())
545  ports.push_back(ServicePortInfo{
546  hw::InnerRefAttr::get(getSymNameAttr(), toClient.getInnerSymAttr()),
547  toClient.getToClientType()});
548 }
549 
550 //===----------------------------------------------------------------------===//
551 // Bundle ops.
552 //===----------------------------------------------------------------------===//
553 
554 static ParseResult
555 parseUnPackBundleType(OpAsmParser &parser,
556  SmallVectorImpl<Type> &toChannelTypes,
557  SmallVectorImpl<Type> &fromChannelTypes, Type &type) {
558 
559  ChannelBundleType bundleType;
560  if (parser.parseType(bundleType))
561  return failure();
562  type = bundleType;
563 
564  for (BundledChannel ch : bundleType.getChannels())
565  if (ch.direction == ChannelDirection::to)
566  toChannelTypes.push_back(ch.type);
567  else if (ch.direction == ChannelDirection::from)
568  fromChannelTypes.push_back(ch.type);
569  else
570  assert(false && "Channel direction invalid");
571  return success();
572 }
573 template <typename T3, typename T4>
574 static void printUnPackBundleType(OpAsmPrinter &p, Operation *, T3, T4,
575  Type bundleType) {
576  p.printType(bundleType);
577 }
578 void UnpackBundleOp::build(::mlir::OpBuilder &odsBuilder,
579  ::mlir::OperationState &odsState, Value bundle,
580  mlir::ValueRange fromChannels) {
581  for (BundledChannel ch :
582  cast<ChannelBundleType>(bundle.getType()).getChannels())
583  if (ch.direction == ChannelDirection::to)
584  odsState.addTypes(ch.type);
585  odsState.addOperands(bundle);
586  odsState.addOperands(fromChannels);
587 }
588 
589 LogicalResult PackBundleOp::verify() {
590  if (!getBundle().hasOneUse())
591  return emitOpError("bundles must have exactly one user");
592  return success();
593 }
594 void PackBundleOp::build(::mlir::OpBuilder &odsBuilder,
595  ::mlir::OperationState &odsState,
596  ChannelBundleType bundleType,
597  mlir::ValueRange toChannels) {
598  odsState.addTypes(bundleType);
599  for (BundledChannel ch : cast<ChannelBundleType>(bundleType).getChannels())
600  if (ch.direction == ChannelDirection::from)
601  odsState.addTypes(ch.type);
602  odsState.addOperands(toChannels);
603 }
604 
605 LogicalResult UnpackBundleOp::verify() {
606  if (!getBundle().hasOneUse())
607  return emitOpError("bundles must have exactly one user");
608  return success();
609 }
610 
612  if (getNumResults() == 0)
613  return;
614  setNameFn(getResult(0), "bundle");
615  for (auto [idx, from] : llvm::enumerate(llvm::make_filter_range(
616  getBundle().getType().getChannels(), [](BundledChannel ch) {
617  return ch.direction == ChannelDirection::from;
618  })))
619  if (idx + 1 < getNumResults())
620  setNameFn(getResult(idx + 1), from.name.getValue());
621 }
622 
624  for (auto [idx, to] : llvm::enumerate(llvm::make_filter_range(
625  getBundle().getType().getChannels(), [](BundledChannel ch) {
626  return ch.direction == ChannelDirection::to;
627  })))
628  if (idx < getNumResults())
629  setNameFn(getResult(idx), to.name.getValue());
630 }
631 //===----------------------------------------------------------------------===//
632 // Structural ops.
633 //===----------------------------------------------------------------------===//
634 
635 LogicalResult ESIPureModuleOp::verify() {
636  ESIDialect *esiDialect = getContext()->getLoadedDialect<ESIDialect>();
637  Block &body = getBody().front();
638  auto channelOrOutput = [](Value v) {
639  if (isa<ChannelType, ChannelBundleType>(v.getType()))
640  return true;
641  if (v.getUsers().empty())
642  return false;
643  return llvm::all_of(v.getUsers(), [](Operation *op) {
644  return isa<ESIPureModuleOutputOp>(op);
645  });
646  };
647 
648  DenseMap<StringAttr, std::tuple<hw::ModulePort::Direction, Type, Operation *>>
649  ports;
650  for (Operation &op : body.getOperations()) {
651  if (igraph::InstanceOpInterface inst =
652  dyn_cast<igraph::InstanceOpInterface>(op)) {
653  if (llvm::any_of(op.getOperands(), [](Value v) {
654  return !(isa<ChannelType, ChannelBundleType>(v.getType()) ||
655  isa<ESIPureModuleInputOp>(v.getDefiningOp()));
656  }))
657  return inst.emitOpError(
658  "instances in ESI pure modules can only contain channel ports or "
659  "ports driven by 'input' ops");
660  if (!llvm::all_of(op.getResults(), channelOrOutput))
661  return inst.emitOpError(
662  "instances in ESI pure modules can only contain channel ports or "
663  "drive only 'outputs'");
664  } else {
665  // Pure modules can only contain instance ops and ESI ops.
666  if (op.getDialect() != esiDialect)
667  return op.emitOpError("operation not allowed in ESI pure modules");
668  }
669 
670  // Check for port validity.
671  if (auto port = dyn_cast<ESIPureModuleInputOp>(op)) {
672  auto existing = ports.find(port.getNameAttr());
673  Type portType = port.getResult().getType();
674  if (existing != ports.end()) {
675  auto [dir, type, op] = existing->getSecond();
676  if (dir != hw::ModulePort::Direction::Input || type != portType)
677  return (port.emitOpError("port '")
678  << port.getName() << "' previously declared as type " << type)
679  .attachNote(op->getLoc());
680  }
681  ports[port.getNameAttr()] = std::make_tuple(
682  hw::ModulePort::Direction::Input, portType, port.getOperation());
683  } else if (auto port = dyn_cast<ESIPureModuleOutputOp>(op)) {
684  auto existing = ports.find(port.getNameAttr());
685  if (existing != ports.end())
686  return (port.emitOpError("port '")
687  << port.getName() << "' previously declared")
688  .attachNote(std::get<2>(existing->getSecond())->getLoc());
689  ports[port.getNameAttr()] =
690  std::make_tuple(hw::ModulePort::Direction::Input,
691  port.getValue().getType(), port.getOperation());
692  }
693  }
694  return success();
695 }
696 
697 hw::ModuleType ESIPureModuleOp::getHWModuleType() {
698  return hw::ModuleType::get(getContext(), {});
699 }
700 
701 SmallVector<::circt::hw::PortInfo> ESIPureModuleOp::getPortList() { return {}; }
703  ::llvm::report_fatal_error("not supported");
704 }
705 
706 size_t ESIPureModuleOp::getNumPorts() { return 0; }
707 size_t ESIPureModuleOp::getNumInputPorts() { return 0; }
708 size_t ESIPureModuleOp::getNumOutputPorts() { return 0; }
709 size_t ESIPureModuleOp::getPortIdForInputId(size_t) {
710  assert(0 && "Out of bounds input port id");
711  return ~0ULL;
712 }
713 size_t ESIPureModuleOp::getPortIdForOutputId(size_t) {
714  assert(0 && "Out of bounds output port id");
715  return ~0ULL;
716 }
717 
718 SmallVector<Location> ESIPureModuleOp::getAllPortLocs() {
719  SmallVector<Location> retval;
720  return retval;
721 }
722 
723 void ESIPureModuleOp::setAllPortLocsAttrs(ArrayRef<Attribute> locs) {
724  emitError("No ports for port locations");
725 }
726 
727 void ESIPureModuleOp::setAllPortNames(ArrayRef<Attribute> names) {
728  emitError("No ports for port naming");
729 }
730 
731 void ESIPureModuleOp::setAllPortAttrs(ArrayRef<Attribute> attrs) {
732  emitError("No ports for port attributes");
733 }
734 
735 void ESIPureModuleOp::removeAllPortAttrs() {
736  emitError("No ports for port attributes)");
737 }
738 
739 ArrayRef<Attribute> ESIPureModuleOp::getAllPortAttrs() { return {}; }
740 
741 void ESIPureModuleOp::setHWModuleType(hw::ModuleType type) {
742  emitError("No ports for port types");
743 }
744 
745 //===----------------------------------------------------------------------===//
746 // Manifest ops.
747 //===----------------------------------------------------------------------===//
748 
749 StringRef ServiceImplRecordOp::getManifestClass() { return "service"; }
750 
751 void ServiceImplRecordOp::getDetails(SmallVectorImpl<NamedAttribute> &results) {
752  auto *ctxt = getContext();
753  // AppID, optionally the service name, implementation name and details.
754  results.emplace_back(getAppIDAttrName(), getAppIDAttr());
755  if (getService())
756  results.emplace_back(getServiceAttrName(), getServiceAttr());
757  results.emplace_back(getServiceImplNameAttrName(), getServiceImplNameAttr());
758  // Don't add another level for the implementation details.
759  for (auto implDetail : getImplDetailsAttr().getValue())
760  results.push_back(implDetail);
761 
762  // All of the manifest data contained by this op.
763  SmallVector<Attribute, 8> reqDetails;
764  for (auto reqDetail : getReqDetails().front().getOps<IsManifestData>())
765  reqDetails.push_back(reqDetail.getDetailsAsDict());
766  results.emplace_back(StringAttr::get(ctxt, "clientDetails"),
767  ArrayAttr::get(ctxt, reqDetails));
768 }
769 
770 bool parseServiceImplRecordReqDetails(OpAsmParser &parser,
771  Region &reqDetailsRegion) {
772  parser.parseOptionalRegion(reqDetailsRegion);
773  if (reqDetailsRegion.empty())
774  reqDetailsRegion.emplaceBlock();
775  return false;
776 }
777 
778 void printServiceImplRecordReqDetails(OpAsmPrinter &p, ServiceImplRecordOp,
779  Region &reqDetailsRegion) {
780  if (!reqDetailsRegion.empty() && !reqDetailsRegion.front().empty())
781  p.printRegion(reqDetailsRegion, /*printEntryBlockArgs=*/false,
782  /*printBlockTerminators=*/false);
783 }
784 
785 StringRef ServiceImplClientRecordOp::getManifestClass() {
786  return "serviceClient";
787 }
788 void ServiceImplClientRecordOp::getDetails(
789  SmallVectorImpl<NamedAttribute> &results) {
790  // Relative AppID path, service port, and implementation details. Don't add
791  // the bundle type since it is meaningless to the host and just clutters the
792  // output.
793  results.emplace_back(getRelAppIDPathAttrName(), getRelAppIDPathAttr());
794  auto servicePort = getServicePortAttr();
795  results.emplace_back(
796  getServicePortAttrName(),
798  getContext(),
799  {
800  NamedAttribute(StringAttr::get(getContext(), "serviceName"),
801  FlatSymbolRefAttr::get(servicePort.getModule())),
802  NamedAttribute(StringAttr::get(getContext(), "port"),
803  servicePort.getName()),
804  }));
805  if (const auto &channelAssignments = getChannelAssignments())
806  results.push_back(
807  NamedAttribute(getChannelAssignmentsAttrName(), *channelAssignments));
808  // Don't add another level for the implementation details.
809  if (const auto &implDetails = getImplDetails())
810  for (const auto &implDetail : *implDetails)
811  results.push_back(implDetail);
812 }
813 
814 StringRef ServiceRequestRecordOp::getManifestClass() { return "clientPort"; }
815 
816 void ServiceRequestRecordOp::getDetails(
817  SmallVectorImpl<NamedAttribute> &results) {
818  auto *ctxt = getContext();
819  results.emplace_back(StringAttr::get(ctxt, "appID"), getRequestorAttr());
820  results.emplace_back(getTypeIDAttrName(), getTypeIDAttr());
821  auto servicePort = getServicePortAttr();
822  results.emplace_back(
823  getServicePortAttrName(),
825  getContext(),
826  {
827  NamedAttribute(StringAttr::get(getContext(), "serviceName"),
828  FlatSymbolRefAttr::get(servicePort.getModule())),
829  NamedAttribute(StringAttr::get(getContext(), "port"),
830  servicePort.getName()),
831  }));
832 }
833 
834 StringRef SymbolMetadataOp::getManifestClass() { return "symInfo"; }
835 
836 StringRef SymbolConstantsOp::getManifestClass() { return "symConsts"; }
837 void SymbolConstantsOp::getDetails(SmallVectorImpl<NamedAttribute> &results) {
838  for (auto &attr : getConstantsAttr())
839  results.push_back(attr);
840 }
841 
842 #define GET_OP_CLASSES
843 #include "circt/Dialect/ESI/ESI.cpp.inc"
844 
845 #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:343
ParseResult parseWrapFIFOType(OpAsmParser &p, Type &dataType, Type &chanInputType)
Definition: ESIOps.cpp:222
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:284
void printServiceImplRecordReqDetails(OpAsmPrinter &p, ServiceImplRecordOp, Region &reqDetailsRegion)
Definition: ESIOps.cpp:778
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:378
static Type getEsiDataType(circt::sv::InterfaceOp iface)
If 'iface' looks like an ESI interface, return the inner data type.
Definition: ESIOps.cpp:269
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:363
static void printUnPackBundleType(OpAsmPrinter &p, Operation *, T3, T4, Type bundleType)
Definition: ESIOps.cpp:574
static ParseResult parseUnPackBundleType(OpAsmParser &parser, SmallVectorImpl< Type > &toChannelTypes, SmallVectorImpl< Type > &fromChannelTypes, Type &type)
Definition: ESIOps.cpp:555
static void printInferWindowRet(OpAsmPrinter &p, Operation *, Type, Type window)
Definition: ESIOps.cpp:352
void printWrapFIFOType(OpAsmPrinter &p, WrapFIFOOp wrap, Type dataType, Type chanType)
Definition: ESIOps.cpp:235
bool parseServiceImplRecordReqDetails(OpAsmParser &parser, Region &reqDetailsRegion)
Definition: ESIOps.cpp:770
static SmallVector< Location > getAllPortLocs(ModTy module)
Definition: HWOps.cpp:1198
static void setAllPortNames(ArrayRef< Attribute > names, ModTy module)
Definition: HWOps.cpp:1268
static void setHWModuleType(ModTy &mod, ModuleType type)
Definition: HWOps.cpp:1341
static SmallVector< PortInfo > getPortList(ModuleTy &mod)
Definition: HWOps.cpp:1420
static PortInfo getPort(ModuleTy &mod, size_t idx)
Definition: HWOps.cpp:1440
@ Input
Definition: HW.h:35
static LogicalResult verify(Value clock, bool eventExists, mlir::Location loc)
Definition: SVOps.cpp:2467
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:55
mlir::Type innerType(mlir::Type type)
Definition: ESITypes.cpp:184
LogicalResult checkBundleTypeMatch(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:486
LogicalResult checkInnerTypeMatch(Type expected, Type actual)
Definition: ESIOps.cpp:393
size_t getNumPorts(Operation *op)
Return the number of ports in a module-like thing (modules, memories, etc)
Definition: FIRRTLOps.cpp:301
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
Definition: esi.py:1
function_ref< void(Value, StringRef)> OpAsmSetValueNameFn
Definition: LLVM.h:182
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.