CIRCT 23.0.0git
Loading...
Searching...
No Matches
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//===----------------------------------------------------------------------===//
249// ValidOnly wrap / unwrap.
250//===----------------------------------------------------------------------===//
251
252circt::esi::ChannelType WrapValidOnlyOp::channelType() {
253 return cast<circt::esi::ChannelType>(getChanOutput().getType());
254}
255
256LogicalResult
257WrapValidOnlyOp::inferReturnTypes(MLIRContext *context, std::optional<Location>,
258 ValueRange operands, DictionaryAttr,
259 mlir::OpaqueProperties, mlir::RegionRange,
260 SmallVectorImpl<Type> &inferredResulTypes) {
261 auto chanType = ChannelType::get(context, operands[0].getType(),
262 ChannelSignaling::ValidOnly, 0);
263 inferredResulTypes.push_back(chanType);
264 return success();
265}
266
267circt::esi::ChannelType UnwrapValidOnlyOp::channelType() {
268 return cast<circt::esi::ChannelType>(getChanInput().getType());
269}
270
271LogicalResult UnwrapValidOnlyOp::verify() {
272 if (getChanInput().getType().getSignaling() != ChannelSignaling::ValidOnly)
273 return emitOpError("only supports ValidOnly signaling");
274 return success();
275}
276
277LogicalResult UnwrapValidOnlyOp::inferReturnTypes(
278 MLIRContext *context, std::optional<Location>, ValueRange operands,
279 DictionaryAttr, mlir::OpaqueProperties, mlir::RegionRange,
280 SmallVectorImpl<Type> &inferredResulTypes) {
281 inferredResulTypes.push_back(
282 cast<ChannelType>(operands[0].getType()).getInner());
283 inferredResulTypes.push_back(
284 IntegerType::get(context, 1, IntegerType::Signless));
285 return success();
286}
287
288/// If 'iface' looks like an ESI interface, return the inner data type.
289static Type getEsiDataType(circt::sv::InterfaceOp iface) {
290 using namespace circt::sv;
291 if (!iface.lookupSymbol<InterfaceSignalOp>("valid"))
292 return Type();
293 if (!iface.lookupSymbol<InterfaceSignalOp>("ready"))
294 return Type();
295 auto dataSig = iface.lookupSymbol<InterfaceSignalOp>("data");
296 if (!dataSig)
297 return Type();
298 return dataSig.getType();
299}
300
301/// Verify that the modport type of 'modportArg' points to an interface which
302/// looks like an ESI interface and the inner data from said interface matches
303/// the chan type's inner data type.
304static LogicalResult verifySVInterface(Operation *op,
305 circt::sv::ModportType modportType,
306 ChannelType chanType) {
307 auto modport =
308 SymbolTable::lookupNearestSymbolFrom<circt::sv::InterfaceModportOp>(
309 op, modportType.getModport());
310 if (!modport)
311 return op->emitError("Could not find modport ")
312 << modportType.getModport() << " in symbol table.";
313 auto iface = cast<circt::sv::InterfaceOp>(modport->getParentOp());
314 Type esiDataType = getEsiDataType(iface);
315 if (!esiDataType)
316 return op->emitOpError("Interface is not a valid ESI interface.");
317 if (esiDataType != chanType.getInner())
318 return op->emitOpError("Operation specifies ")
319 << chanType << " but type inside doesn't match interface data type "
320 << esiDataType << ".";
321 return success();
322}
323
324LogicalResult WrapSVInterfaceOp::verify() {
325 auto modportType = cast<circt::sv::ModportType>(getInterfaceSink().getType());
326 auto chanType = cast<ChannelType>(getOutput().getType());
327 return verifySVInterface(*this, modportType, chanType);
328}
329
330circt::esi::ChannelType WrapSVInterfaceOp::channelType() {
331 return cast<circt::esi::ChannelType>(getOutput().getType());
332}
333
334LogicalResult UnwrapSVInterfaceOp::verify() {
335 auto modportType =
336 cast<circt::sv::ModportType>(getInterfaceSource().getType());
337 auto chanType = cast<ChannelType>(getChanInput().getType());
338 return verifySVInterface(*this, modportType, chanType);
339}
340
341circt::esi::ChannelType UnwrapSVInterfaceOp::channelType() {
342 return cast<circt::esi::ChannelType>(getChanInput().getType());
343}
344
345LogicalResult WrapWindow::verify() {
346 Type expectedInput = getWindow().getType().getLoweredType();
347 if (expectedInput == getFrame().getType())
348 return success();
349 return emitOpError("Expected input type is ") << expectedInput;
350}
351
352LogicalResult
353UnwrapWindow::inferReturnTypes(MLIRContext *, std::optional<Location>,
354 ValueRange operands, DictionaryAttr,
355 mlir::OpaqueProperties, mlir::RegionRange,
356 SmallVectorImpl<Type> &inferredReturnTypes) {
357 auto windowType = cast<WindowType>(operands.front().getType());
358 inferredReturnTypes.push_back(windowType.getLoweredType());
359 return success();
360}
361
362/// Determine the input type ('frame') from the return type ('window').
363static bool parseInferWindowRet(OpAsmParser &p, Type &frame, Type &windowOut) {
364 WindowType window;
365 if (p.parseType(window))
366 return true;
367 windowOut = window;
368 frame = window.getLoweredType();
369 return false;
370}
371
372static void printInferWindowRet(OpAsmPrinter &p, Operation *, Type,
373 Type window) {
374 p << window;
375}
376
377//===----------------------------------------------------------------------===//
378// Cosim ops.
379//===----------------------------------------------------------------------===//
380
381LogicalResult CosimFromHostEndpointOp::verify() {
382 ChannelType chanType = getFromHost().getType();
383 if (chanType.getSignaling() != ChannelSignaling::ValidReady)
384 return emitOpError("only supports valid-ready signaling");
385 return success();
386}
387
388LogicalResult CosimToHostEndpointOp::verify() {
389 ChannelType chanType = getToHost().getType();
390 if (chanType.getSignaling() != ChannelSignaling::ValidReady)
391 return emitOpError("only supports valid-ready signaling");
392 return success();
393}
394
395//===----------------------------------------------------------------------===//
396// Services ops.
397//===----------------------------------------------------------------------===//
398
399/// Get the port declaration op for the specified service decl, port name.
400static FailureOr<ServiceDeclOpInterface>
401getServiceDecl(Operation *op, SymbolTableCollection &symbolTable,
402 hw::InnerRefAttr servicePort) {
403 ModuleOp top = op->getParentOfType<mlir::ModuleOp>();
404 SymbolTable &topSyms = symbolTable.getSymbolTable(top);
405
406 StringAttr modName = servicePort.getModule();
407 auto serviceDecl = topSyms.lookup<ServiceDeclOpInterface>(modName);
408 if (!serviceDecl)
409 return op->emitOpError("Could not find service declaration ")
410 << servicePort.getModuleRef();
411 return serviceDecl;
412}
413
414/// Get the port info for the specified service decl and port name.
415static FailureOr<ServicePortInfo>
416getServicePortInfo(Operation *op, SymbolTableCollection &symbolTable,
417 hw::InnerRefAttr servicePort) {
418 auto serviceDecl = getServiceDecl(op, symbolTable, servicePort);
419 if (failed(serviceDecl))
420 return failure();
421 auto portInfo = serviceDecl->getPortInfo(servicePort.getName());
422 if (failed(portInfo))
423 return op->emitOpError("Could not locate port ") << servicePort.getName();
424 return portInfo;
425}
426
427namespace circt {
428namespace esi {
429// Check that the channels on two bundles match allowing for AnyType.
430// NOLINTNEXTLINE(misc-no-recursion)
431LogicalResult checkInnerTypeMatch(Type expected, Type actual) {
432 if (expected == actual)
433 return success();
434
435 // Check all of the 'container' or special case types.
436 return TypeSwitch<Type, LogicalResult>(expected)
437 // For 'any' types, we can match anything.
438 .Case<AnyType>([&](Type) { return success(); })
439 // If they're both channels, check the inner types.
440 .Case<ChannelType>([&](ChannelType expectedChannel) {
441 auto actualChannel = dyn_cast<ChannelType>(actual);
442 if (!actualChannel)
443 return failure();
444 return checkInnerTypeMatch(expectedChannel.getInner(),
445 actualChannel.getInner());
446 })
447 // For structs, check each field.
448 .Case<hw::StructType>([&](hw::StructType expectedStruct) {
449 auto actualStruct = dyn_cast<hw::StructType>(actual);
450 if (!actualStruct)
451 return failure();
452 auto expectedFields = expectedStruct.getElements();
453 auto actualFields = actualStruct.getElements();
454 if (expectedFields.size() != actualFields.size())
455 return failure();
456 for (auto [efield, afield] : llvm::zip(expectedFields, actualFields)) {
457 if (efield.name != afield.name)
458 return failure();
459 if (failed(checkInnerTypeMatch(efield.type, afield.type)))
460 return failure();
461 }
462 return success();
463 })
464 // For arrays, check the element type and size.
465 .Case<hw::ArrayType>([&](hw::ArrayType expectedArray) {
466 auto actualArray = dyn_cast<hw::ArrayType>(actual);
467 if (!actualArray)
468 return failure();
469 if (expectedArray.getNumElements() != actualArray.getNumElements())
470 return failure();
471 return checkInnerTypeMatch(expectedArray.getElementType(),
472 actualArray.getElementType());
473 })
474 // For unions, check the element types and names.
475 .Case<hw::UnionType>([&](hw::UnionType expectedUnion) {
476 auto actualUnion = dyn_cast<hw::UnionType>(actual);
477 if (!actualUnion)
478 return failure();
479 auto expectedElements = expectedUnion.getElements();
480 auto actualElements = actualUnion.getElements();
481 if (expectedElements.size() != actualElements.size())
482 return failure();
483 for (auto [efield, afield] :
484 llvm::zip(expectedElements, actualElements)) {
485 if (efield.name != afield.name)
486 return failure();
487 if (efield.offset != afield.offset)
488 return failure();
489 if (failed(checkInnerTypeMatch(efield.type, afield.type)))
490 return failure();
491 }
492 return success();
493 })
494 // For ESI lists, check the element type.
495 .Case<ListType>([&](ListType expectedList) {
496 auto actualList = dyn_cast<ListType>(actual);
497 if (!actualList)
498 return failure();
499 return checkInnerTypeMatch(expectedList.getElementType(),
500 actualList.getElementType());
501 })
502 // For ESI windows, unwrap and check the inner type.
503 .Case<WindowType>([&](WindowType expectedWindow) {
504 auto actualWindow = dyn_cast<WindowType>(actual);
505 if (!actualWindow)
506 return checkInnerTypeMatch(expectedWindow.getInto(), actual);
507 return checkInnerTypeMatch(expectedWindow.getInto(),
508 actualWindow.getInto());
509 })
510 // For type aliases, unwrap and check the aliased type.
511 .Case<hw::TypeAliasType>([&](hw::TypeAliasType expectedAlias) {
512 auto actualAlias = dyn_cast<hw::TypeAliasType>(actual);
513 if (!actualAlias)
514 return checkInnerTypeMatch(expectedAlias.getCanonicalType(), actual);
515 return checkInnerTypeMatch(expectedAlias.getCanonicalType(),
516 actualAlias.getCanonicalType());
517 })
518 // TODO: other container types.
519 .Default([&](Type) { return failure(); });
520}
521
522/// Check that the channels on two bundles match allowing for AnyType in the
523/// 'svc' bundle.
524LogicalResult checkBundleTypeMatch(Operation *req,
525 ChannelBundleType svcBundleType,
526 ChannelBundleType reqBundleType,
527 bool skipDirectionCheck) {
528 if (svcBundleType.getChannels().size() != reqBundleType.getChannels().size())
529 return req->emitOpError(
530 "Request port bundle channel count does not match service "
531 "port bundle channel count");
532
533 // Build fast lookup.
534 DenseMap<StringAttr, BundledChannel> declBundleChannels;
535 for (BundledChannel bc : svcBundleType.getChannels())
536 declBundleChannels[bc.name] = bc;
537
538 // Check all the channels.
539 for (BundledChannel bc : reqBundleType.getChannels()) {
540 auto f = declBundleChannels.find(bc.name);
541 if (f == declBundleChannels.end())
542 return req->emitOpError(
543 "Request channel name not found in service port bundle");
544 if (!skipDirectionCheck && f->second.direction != bc.direction)
545 return req->emitOpError(
546 "Request channel direction does not match service "
547 "port bundle channel direction");
548
549 if (failed(checkInnerTypeMatch(f->second.type, bc.type)))
550 return req->emitOpError(
551 "Request channel type does not match service port "
552 "bundle channel type")
553 .attachNote()
554 << "Service port '" << bc.name.getValue()
555 << "' type: " << f->second.type;
556 }
557 return success();
558}
559
560} // namespace esi
561} // namespace circt
562
563LogicalResult
564RequestConnectionOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
565 auto svcPort = getServicePortInfo(*this, symbolTable, getServicePortAttr());
566 if (failed(svcPort))
567 return failure();
568 return checkBundleTypeMatch(*this, svcPort->type, getToClient().getType(),
569 false);
570}
571
572LogicalResult ServiceImplementConnReqOp::verifySymbolUses(
573 SymbolTableCollection &symbolTable) {
574 auto svcPort = getServicePortInfo(*this, symbolTable, getServicePortAttr());
575 if (failed(svcPort))
576 return failure();
577 return checkBundleTypeMatch(*this, svcPort->type, getToClient().getType(),
578 true);
579}
580
581void CustomServiceDeclOp::getPortList(SmallVectorImpl<ServicePortInfo> &ports) {
582 for (auto toClient : getOps<ServiceDeclPortOp>())
583 ports.push_back(ServicePortInfo{
584 hw::InnerRefAttr::get(getSymNameAttr(), toClient.getInnerSymAttr()),
585 toClient.getToClientType()});
586}
587
588//===----------------------------------------------------------------------===//
589// Bundle ops.
590//===----------------------------------------------------------------------===//
591
592static ParseResult
593parseUnPackBundleType(OpAsmParser &parser,
594 SmallVectorImpl<Type> &toChannelTypes,
595 SmallVectorImpl<Type> &fromChannelTypes, Type &type) {
596
597 ChannelBundleType bundleType;
598 if (parser.parseType(bundleType))
599 return failure();
600 type = bundleType;
601
602 for (BundledChannel ch : bundleType.getChannels())
603 if (ch.direction == ChannelDirection::to)
604 toChannelTypes.push_back(ch.type);
605 else if (ch.direction == ChannelDirection::from)
606 fromChannelTypes.push_back(ch.type);
607 else
608 assert(false && "Channel direction invalid");
609 return success();
610}
611template <typename T3, typename T4>
612static void printUnPackBundleType(OpAsmPrinter &p, Operation *, T3, T4,
613 Type bundleType) {
614 p.printType(bundleType);
615}
616void UnpackBundleOp::build(::mlir::OpBuilder &odsBuilder,
617 ::mlir::OperationState &odsState, Value bundle,
618 mlir::ValueRange fromChannels) {
619 for (BundledChannel ch :
620 cast<ChannelBundleType>(bundle.getType()).getChannels())
621 if (ch.direction == ChannelDirection::to)
622 odsState.addTypes(ch.type);
623 odsState.addOperands(bundle);
624 odsState.addOperands(fromChannels);
625}
626
627LogicalResult PackBundleOp::verify() {
628 if (!getBundle().hasOneUse()) {
629 if (getBundle().getUsers().empty())
630 return emitOpError("bundle must be used");
631 return emitOpError("bundle must only be used once");
632 }
633 return success();
634}
635void PackBundleOp::build(::mlir::OpBuilder &odsBuilder,
636 ::mlir::OperationState &odsState,
637 ChannelBundleType bundleType,
638 mlir::ValueRange toChannels) {
639 odsState.addTypes(bundleType);
640 for (BundledChannel ch : cast<ChannelBundleType>(bundleType).getChannels())
641 if (ch.direction == ChannelDirection::from)
642 odsState.addTypes(ch.type);
643 odsState.addOperands(toChannels);
644}
645
646LogicalResult UnpackBundleOp::verify() {
647 if (!getBundle().hasOneUse()) {
648 if (getBundle().getUsers().empty())
649 return emitOpError("bundle must be used");
650 return emitOpError("bundle must only be used once");
651 }
652 return success();
653}
654
655void PackBundleOp::getAsmResultNames(::mlir::OpAsmSetValueNameFn setNameFn) {
656 if (getNumResults() == 0)
657 return;
658 setNameFn(getResult(0), "bundle");
659 for (auto [idx, from] : llvm::enumerate(llvm::make_filter_range(
660 getBundle().getType().getChannels(), [](BundledChannel ch) {
661 return ch.direction == ChannelDirection::from;
662 })))
663 if (idx + 1 < getNumResults())
664 setNameFn(getResult(idx + 1), from.name.getValue());
665}
666
667void UnpackBundleOp::getAsmResultNames(::mlir::OpAsmSetValueNameFn setNameFn) {
668 for (auto [idx, to] : llvm::enumerate(llvm::make_filter_range(
669 getBundle().getType().getChannels(), [](BundledChannel ch) {
670 return ch.direction == ChannelDirection::to;
671 })))
672 if (idx < getNumResults())
673 setNameFn(getResult(idx), to.name.getValue());
674}
675//===----------------------------------------------------------------------===//
676// Structural ops.
677//===----------------------------------------------------------------------===//
678
679LogicalResult ESIPureModuleOp::verify() {
680 ESIDialect *esiDialect = getContext()->getLoadedDialect<ESIDialect>();
681 Block &body = getBody().front();
682 auto channelOrOutput = [](Value v) {
683 if (isa<ChannelType, ChannelBundleType>(v.getType()))
684 return true;
685 if (v.getUsers().empty())
686 return false;
687 return llvm::all_of(v.getUsers(), [](Operation *op) {
688 return isa<ESIPureModuleOutputOp>(op);
689 });
690 };
691
692 DenseMap<StringAttr, std::tuple<hw::ModulePort::Direction, Type, Operation *>>
693 ports;
694 for (Operation &op : body.getOperations()) {
695 if (igraph::InstanceOpInterface inst =
696 dyn_cast<igraph::InstanceOpInterface>(op)) {
697 if (llvm::any_of(op.getOperands(), [](Value v) {
698 return !(isa<ChannelType, ChannelBundleType>(v.getType()) ||
699 isa<ESIPureModuleInputOp>(v.getDefiningOp()));
700 }))
701 return inst.emitOpError(
702 "instances in ESI pure modules can only contain channel ports or "
703 "ports driven by 'input' ops");
704 if (!llvm::all_of(op.getResults(), channelOrOutput))
705 return inst.emitOpError(
706 "instances in ESI pure modules can only contain channel ports or "
707 "drive only 'outputs'");
708 } else {
709 // Pure modules can only contain instance ops and ESI ops.
710 if (op.getDialect() != esiDialect)
711 return op.emitOpError("operation not allowed in ESI pure modules");
712 }
713
714 // Check for port validity.
715 if (auto port = dyn_cast<ESIPureModuleInputOp>(op)) {
716 auto existing = ports.find(port.getNameAttr());
717 Type portType = port.getResult().getType();
718 if (existing != ports.end()) {
719 auto [dir, type, op] = existing->getSecond();
720 if (dir != hw::ModulePort::Direction::Input || type != portType)
721 return (port.emitOpError("port '")
722 << port.getName() << "' previously declared as type " << type)
723 .attachNote(op->getLoc());
724 }
725 ports[port.getNameAttr()] = std::make_tuple(
726 hw::ModulePort::Direction::Input, portType, port.getOperation());
727 } else if (auto port = dyn_cast<ESIPureModuleOutputOp>(op)) {
728 auto existing = ports.find(port.getNameAttr());
729 if (existing != ports.end())
730 return (port.emitOpError("port '")
731 << port.getName() << "' previously declared")
732 .attachNote(std::get<2>(existing->getSecond())->getLoc());
733 ports[port.getNameAttr()] =
734 std::make_tuple(hw::ModulePort::Direction::Input,
735 port.getValue().getType(), port.getOperation());
736 }
737 }
738 return success();
739}
740
741hw::ModuleType ESIPureModuleOp::getHWModuleType() {
742 return hw::ModuleType::get(getContext(), {});
743}
744
745SmallVector<::circt::hw::PortInfo> ESIPureModuleOp::getPortList() { return {}; }
746::circt::hw::PortInfo ESIPureModuleOp::getPort(size_t idx) {
747 ::llvm::report_fatal_error("not supported");
748}
749
750size_t ESIPureModuleOp::getNumPorts() { return 0; }
751size_t ESIPureModuleOp::getNumInputPorts() { return 0; }
752size_t ESIPureModuleOp::getNumOutputPorts() { return 0; }
753size_t ESIPureModuleOp::getPortIdForInputId(size_t) {
754 assert(0 && "Out of bounds input port id");
755 return ~0ULL;
756}
757size_t ESIPureModuleOp::getPortIdForOutputId(size_t) {
758 assert(0 && "Out of bounds output port id");
759 return ~0ULL;
760}
761
762SmallVector<Location> ESIPureModuleOp::getAllPortLocs() {
763 SmallVector<Location> retval;
764 return retval;
765}
766
767void ESIPureModuleOp::setAllPortLocsAttrs(ArrayRef<Attribute> locs) {
768 emitError("No ports for port locations");
769}
770
771void ESIPureModuleOp::setAllPortNames(ArrayRef<Attribute> names) {
772 emitError("No ports for port naming");
773}
774
775void ESIPureModuleOp::setAllPortAttrs(ArrayRef<Attribute> attrs) {
776 emitError("No ports for port attributes");
777}
778
779void ESIPureModuleOp::removeAllPortAttrs() {
780 emitError("No ports for port attributes)");
781}
782
783ArrayRef<Attribute> ESIPureModuleOp::getAllPortAttrs() { return {}; }
784
785void ESIPureModuleOp::setHWModuleType(hw::ModuleType type) {
786 emitError("No ports for port types");
787}
788
789//===----------------------------------------------------------------------===//
790// Manifest ops.
791//===----------------------------------------------------------------------===//
792
793StringRef ServiceImplRecordOp::getManifestClass() {
794 return getIsEngine() ? "engine" : "service";
795}
796
797void ServiceImplRecordOp::getDetails(SmallVectorImpl<NamedAttribute> &results) {
798 auto *ctxt = getContext();
799 // AppID, optionally the service name, implementation name and details.
800 results.emplace_back(getAppIDAttrName(), getAppIDAttr());
801 if (getService())
802 results.emplace_back(getServiceAttrName(), getServiceAttr());
803 results.emplace_back(getServiceImplNameAttrName(), getServiceImplNameAttr());
804 // Don't add another level for the implementation details.
805 for (auto implDetail : getImplDetailsAttr().getValue())
806 results.push_back(implDetail);
807
808 // All of the manifest data contained by this op.
809 SmallVector<Attribute, 8> reqDetails;
810 for (auto reqDetail : getReqDetails().front().getOps<IsManifestData>())
811 reqDetails.push_back(reqDetail.getDetailsAsDict());
812 results.emplace_back(StringAttr::get(ctxt, "clientDetails"),
813 ArrayAttr::get(ctxt, reqDetails));
814}
815
816bool parseServiceImplRecordReqDetails(OpAsmParser &parser,
817 Region &reqDetailsRegion) {
818 parser.parseOptionalRegion(reqDetailsRegion);
819 if (reqDetailsRegion.empty())
820 reqDetailsRegion.emplaceBlock();
821 return false;
822}
823
824void printServiceImplRecordReqDetails(OpAsmPrinter &p, ServiceImplRecordOp,
825 Region &reqDetailsRegion) {
826 if (!reqDetailsRegion.empty() && !reqDetailsRegion.front().empty())
827 p.printRegion(reqDetailsRegion, /*printEntryBlockArgs=*/false,
828 /*printBlockTerminators=*/false);
829}
830
831StringRef ServiceImplClientRecordOp::getManifestClass() {
832 return "serviceClient";
833}
834void ServiceImplClientRecordOp::getDetails(
835 SmallVectorImpl<NamedAttribute> &results) {
836 // Relative AppID path, service port, and implementation details. Don't add
837 // the bundle type since it is meaningless to the host and just clutters the
838 // output.
839 results.emplace_back(getRelAppIDPathAttrName(), getRelAppIDPathAttr());
840 auto servicePort = getServicePortAttr();
841 results.emplace_back(
842 getServicePortAttrName(),
843 DictionaryAttr::get(
844 getContext(),
845 {
846 NamedAttribute(StringAttr::get(getContext(), "serviceName"),
847 FlatSymbolRefAttr::get(servicePort.getModule())),
848 NamedAttribute(StringAttr::get(getContext(), "port"),
849 servicePort.getName()),
850 }));
851 if (const auto &channelAssignments = getChannelAssignments())
852 results.push_back(
853 NamedAttribute(getChannelAssignmentsAttrName(), *channelAssignments));
854 // Don't add another level for the implementation details.
855 if (const auto &implDetails = getImplDetails())
856 for (const auto &implDetail : *implDetails)
857 results.push_back(implDetail);
858}
859
860StringRef ServiceRequestRecordOp::getManifestClass() { return "clientPort"; }
861
862void ServiceRequestRecordOp::getDetails(
863 SmallVectorImpl<NamedAttribute> &results) {
864 auto *ctxt = getContext();
865 results.emplace_back(StringAttr::get(ctxt, "appID"), getRequestorAttr());
866 results.emplace_back(getTypeIDAttrName(), getTypeIDAttr());
867 auto servicePort = getServicePortAttr();
868 results.emplace_back(
869 getServicePortAttrName(),
870 DictionaryAttr::get(
871 getContext(),
872 {
873 NamedAttribute(StringAttr::get(getContext(), "serviceName"),
874 FlatSymbolRefAttr::get(servicePort.getModule())),
875 NamedAttribute(StringAttr::get(getContext(), "port"),
876 servicePort.getName()),
877 }));
878}
879
880StringRef SymbolMetadataOp::getManifestClass() { return "symInfo"; }
881
882StringRef SymbolConstantsOp::getManifestClass() { return "symConsts"; }
883void SymbolConstantsOp::getDetails(SmallVectorImpl<NamedAttribute> &results) {
884 for (auto &attr : getConstantsAttr())
885 results.push_back(attr);
886}
887
888#define GET_OP_CLASSES
889#include "circt/Dialect/ESI/ESI.cpp.inc"
890
891#include "circt/Dialect/ESI/ESIInterfaces.cpp.inc"
assert(baseType &&"element must be base type")
return wrap(CMemoryType::get(unwrap(ctx), baseType, numElements))
static std::unique_ptr< Context > context
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:401
static bool parseInferWindowRet(OpAsmParser &p, Type &frame, Type &windowOut)
Determine the input type ('frame') from the return type ('window').
Definition ESIOps.cpp:363
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:304
void printServiceImplRecordReqDetails(OpAsmPrinter &p, ServiceImplRecordOp, Region &reqDetailsRegion)
Definition ESIOps.cpp:824
static Type getEsiDataType(circt::sv::InterfaceOp iface)
If 'iface' looks like an ESI interface, return the inner data type.
Definition ESIOps.cpp:289
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:416
static void printUnPackBundleType(OpAsmPrinter &p, Operation *, T3, T4, Type bundleType)
Definition ESIOps.cpp:612
static ParseResult parseUnPackBundleType(OpAsmParser &parser, SmallVectorImpl< Type > &toChannelTypes, SmallVectorImpl< Type > &fromChannelTypes, Type &type)
Definition ESIOps.cpp:593
static void printInferWindowRet(OpAsmPrinter &p, Operation *, Type, Type window)
Definition ESIOps.cpp:372
void printWrapFIFOType(OpAsmPrinter &p, WrapFIFOOp wrap, Type dataType, Type chanType)
Definition ESIOps.cpp:215
bool parseServiceImplRecordReqDetails(OpAsmParser &parser, Region &reqDetailsRegion)
Definition ESIOps.cpp:816
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:422
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:524
LogicalResult checkInnerTypeMatch(Type expected, Type actual)
Definition ESIOps.cpp:431
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.