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