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