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::PropertyRef 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::PropertyRef 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::PropertyRef, 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::PropertyRef, 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::PropertyRef, 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::PropertyRef, 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 if (failed(checkBundleTypeMatch(*this, svcPort->type, getToClient().getType(),
569 false)))
570 return failure();
571 auto svcDecl = getServiceDecl(*this, symbolTable, getServicePortAttr());
572 if (failed(svcDecl))
573 return failure();
574 return svcDecl->verifyRequest(*svcPort, getToClient().getType(), *this);
575}
576
577LogicalResult ServiceImplementConnReqOp::verifySymbolUses(
578 SymbolTableCollection &symbolTable) {
579 auto svcPort = getServicePortInfo(*this, symbolTable, getServicePortAttr());
580 if (failed(svcPort))
581 return failure();
582 if (failed(checkBundleTypeMatch(*this, svcPort->type, getToClient().getType(),
583 true)))
584 return failure();
585 auto svcDecl = getServiceDecl(*this, symbolTable, getServicePortAttr());
586 if (failed(svcDecl))
587 return failure();
588 return svcDecl->verifyRequest(*svcPort, getToClient().getType(), *this);
589}
590
591void CustomServiceDeclOp::getPortList(SmallVectorImpl<ServicePortInfo> &ports) {
592 for (auto toClient : getOps<ServiceDeclPortOp>())
593 ports.push_back(ServicePortInfo{
594 hw::InnerRefAttr::get(getSymNameAttr(), toClient.getInnerSymAttr()),
595 toClient.getToClientType()});
596}
597
598//===----------------------------------------------------------------------===//
599// Bundle ops.
600//===----------------------------------------------------------------------===//
601
602static ParseResult
603parseUnPackBundleType(OpAsmParser &parser,
604 SmallVectorImpl<Type> &toChannelTypes,
605 SmallVectorImpl<Type> &fromChannelTypes, Type &type) {
606
607 ChannelBundleType bundleType;
608 if (parser.parseType(bundleType))
609 return failure();
610 type = bundleType;
611
612 for (BundledChannel ch : bundleType.getChannels())
613 if (ch.direction == ChannelDirection::to)
614 toChannelTypes.push_back(ch.type);
615 else if (ch.direction == ChannelDirection::from)
616 fromChannelTypes.push_back(ch.type);
617 else
618 assert(false && "Channel direction invalid");
619 return success();
620}
621template <typename T3, typename T4>
622static void printUnPackBundleType(OpAsmPrinter &p, Operation *, T3, T4,
623 Type bundleType) {
624 p.printType(bundleType);
625}
626void UnpackBundleOp::build(::mlir::OpBuilder &odsBuilder,
627 ::mlir::OperationState &odsState, Value bundle,
628 mlir::ValueRange fromChannels) {
629 for (BundledChannel ch :
630 cast<ChannelBundleType>(bundle.getType()).getChannels())
631 if (ch.direction == ChannelDirection::to)
632 odsState.addTypes(ch.type);
633 odsState.addOperands(bundle);
634 odsState.addOperands(fromChannels);
635}
636
637LogicalResult PackBundleOp::verify() {
638 if (!getBundle().hasOneUse()) {
639 if (getBundle().getUsers().empty())
640 return emitOpError("bundle must be used");
641 return emitOpError("bundle must only be used once");
642 }
643 return success();
644}
645void PackBundleOp::build(::mlir::OpBuilder &odsBuilder,
646 ::mlir::OperationState &odsState,
647 ChannelBundleType bundleType,
648 mlir::ValueRange toChannels) {
649 odsState.addTypes(bundleType);
650 for (BundledChannel ch : cast<ChannelBundleType>(bundleType).getChannels())
651 if (ch.direction == ChannelDirection::from)
652 odsState.addTypes(ch.type);
653 odsState.addOperands(toChannels);
654}
655
656LogicalResult UnpackBundleOp::verify() {
657 if (!getBundle().hasOneUse()) {
658 if (getBundle().getUsers().empty())
659 return emitOpError("bundle must be used");
660 return emitOpError("bundle must only be used once");
661 }
662 return success();
663}
664
665void PackBundleOp::getAsmResultNames(::mlir::OpAsmSetValueNameFn setNameFn) {
666 if (getNumResults() == 0)
667 return;
668 setNameFn(getResult(0), "bundle");
669 for (auto [idx, from] : llvm::enumerate(llvm::make_filter_range(
670 getBundle().getType().getChannels(), [](BundledChannel ch) {
671 return ch.direction == ChannelDirection::from;
672 })))
673 if (idx + 1 < getNumResults())
674 setNameFn(getResult(idx + 1), from.name.getValue());
675}
676
677void UnpackBundleOp::getAsmResultNames(::mlir::OpAsmSetValueNameFn setNameFn) {
678 for (auto [idx, to] : llvm::enumerate(llvm::make_filter_range(
679 getBundle().getType().getChannels(), [](BundledChannel ch) {
680 return ch.direction == ChannelDirection::to;
681 })))
682 if (idx < getNumResults())
683 setNameFn(getResult(idx), to.name.getValue());
684}
685//===----------------------------------------------------------------------===//
686// Structural ops.
687//===----------------------------------------------------------------------===//
688
689LogicalResult ESIPureModuleOp::verify() {
690 ESIDialect *esiDialect = getContext()->getLoadedDialect<ESIDialect>();
691 Block &body = getBody().front();
692 auto channelOrOutput = [](Value v) {
693 if (isa<ChannelType, ChannelBundleType>(v.getType()))
694 return true;
695 if (v.getUsers().empty())
696 return false;
697 return llvm::all_of(v.getUsers(), [](Operation *op) {
698 return isa<ESIPureModuleOutputOp>(op);
699 });
700 };
701
702 DenseMap<StringAttr, std::tuple<hw::ModulePort::Direction, Type, Operation *>>
703 ports;
704 for (Operation &op : body.getOperations()) {
705 if (igraph::InstanceOpInterface inst =
706 dyn_cast<igraph::InstanceOpInterface>(op)) {
707 if (llvm::any_of(op.getOperands(), [](Value v) {
708 return !(isa<ChannelType, ChannelBundleType>(v.getType()) ||
709 isa<ESIPureModuleInputOp>(v.getDefiningOp()));
710 }))
711 return inst.emitOpError(
712 "instances in ESI pure modules can only contain channel ports or "
713 "ports driven by 'input' ops");
714 if (!llvm::all_of(op.getResults(), channelOrOutput))
715 return inst.emitOpError(
716 "instances in ESI pure modules can only contain channel ports or "
717 "drive only 'outputs'");
718 } else {
719 // Pure modules can only contain instance ops and ESI ops.
720 if (op.getDialect() != esiDialect)
721 return op.emitOpError("operation not allowed in ESI pure modules");
722 }
723
724 // Check for port validity.
725 if (auto port = dyn_cast<ESIPureModuleInputOp>(op)) {
726 auto existing = ports.find(port.getNameAttr());
727 Type portType = port.getResult().getType();
728 if (existing != ports.end()) {
729 auto [dir, type, op] = existing->getSecond();
730 if (dir != hw::ModulePort::Direction::Input || type != portType)
731 return (port.emitOpError("port '")
732 << port.getName() << "' previously declared as type " << type)
733 .attachNote(op->getLoc());
734 }
735 ports[port.getNameAttr()] = std::make_tuple(
736 hw::ModulePort::Direction::Input, portType, port.getOperation());
737 } else if (auto port = dyn_cast<ESIPureModuleOutputOp>(op)) {
738 auto existing = ports.find(port.getNameAttr());
739 if (existing != ports.end())
740 return (port.emitOpError("port '")
741 << port.getName() << "' previously declared")
742 .attachNote(std::get<2>(existing->getSecond())->getLoc());
743 ports[port.getNameAttr()] =
744 std::make_tuple(hw::ModulePort::Direction::Input,
745 port.getValue().getType(), port.getOperation());
746 }
747 }
748 return success();
749}
750
751hw::ModuleType ESIPureModuleOp::getHWModuleType() {
752 return hw::ModuleType::get(getContext(), {});
753}
754
755SmallVector<::circt::hw::PortInfo> ESIPureModuleOp::getPortList() { return {}; }
756::circt::hw::PortInfo ESIPureModuleOp::getPort(size_t idx) {
757 ::llvm::report_fatal_error("not supported");
758}
759
760size_t ESIPureModuleOp::getNumPorts() { return 0; }
761size_t ESIPureModuleOp::getNumInputPorts() { return 0; }
762size_t ESIPureModuleOp::getNumOutputPorts() { return 0; }
763size_t ESIPureModuleOp::getPortIdForInputId(size_t) {
764 assert(0 && "Out of bounds input port id");
765 return ~0ULL;
766}
767size_t ESIPureModuleOp::getPortIdForOutputId(size_t) {
768 assert(0 && "Out of bounds output port id");
769 return ~0ULL;
770}
771
772SmallVector<Location> ESIPureModuleOp::getAllPortLocs() {
773 SmallVector<Location> retval;
774 return retval;
775}
776
777void ESIPureModuleOp::setAllPortLocsAttrs(ArrayRef<Attribute> locs) {
778 emitError("No ports for port locations");
779}
780
781void ESIPureModuleOp::setAllPortNames(ArrayRef<Attribute> names) {
782 emitError("No ports for port naming");
783}
784
785void ESIPureModuleOp::setAllPortAttrs(ArrayRef<Attribute> attrs) {
786 emitError("No ports for port attributes");
787}
788
789void ESIPureModuleOp::removeAllPortAttrs() {
790 emitError("No ports for port attributes)");
791}
792
793ArrayRef<Attribute> ESIPureModuleOp::getAllPortAttrs() { return {}; }
794
795void ESIPureModuleOp::setHWModuleType(hw::ModuleType type) {
796 emitError("No ports for port types");
797}
798
799//===----------------------------------------------------------------------===//
800// Manifest ops.
801//===----------------------------------------------------------------------===//
802
803StringRef ServiceImplRecordOp::getManifestClass() {
804 return getIsEngine() ? "engine" : "service";
805}
806
807void ServiceImplRecordOp::getDetails(SmallVectorImpl<NamedAttribute> &results) {
808 auto *ctxt = getContext();
809 // AppID, optionally the service name, implementation name and details.
810 results.emplace_back(getAppIDAttrName(), getAppIDAttr());
811 if (getService())
812 results.emplace_back(getServiceAttrName(), getServiceAttr());
813 results.emplace_back(getServiceImplNameAttrName(), getServiceImplNameAttr());
814 // Don't add another level for the implementation details.
815 for (auto implDetail : getImplDetailsAttr().getValue())
816 results.push_back(implDetail);
817
818 // All of the manifest data contained by this op.
819 SmallVector<Attribute, 8> reqDetails;
820 for (auto reqDetail : getReqDetails().front().getOps<IsManifestData>())
821 reqDetails.push_back(reqDetail.getDetailsAsDict());
822 results.emplace_back(StringAttr::get(ctxt, "clientDetails"),
823 ArrayAttr::get(ctxt, reqDetails));
824}
825
826bool parseServiceImplRecordReqDetails(OpAsmParser &parser,
827 Region &reqDetailsRegion) {
828 parser.parseOptionalRegion(reqDetailsRegion);
829 if (reqDetailsRegion.empty())
830 reqDetailsRegion.emplaceBlock();
831 return false;
832}
833
834void printServiceImplRecordReqDetails(OpAsmPrinter &p, ServiceImplRecordOp,
835 Region &reqDetailsRegion) {
836 if (!reqDetailsRegion.empty() && !reqDetailsRegion.front().empty())
837 p.printRegion(reqDetailsRegion, /*printEntryBlockArgs=*/false,
838 /*printBlockTerminators=*/false);
839}
840
841StringRef ServiceImplClientRecordOp::getManifestClass() {
842 return "serviceClient";
843}
844void ServiceImplClientRecordOp::getDetails(
845 SmallVectorImpl<NamedAttribute> &results) {
846 // Relative AppID path, service port, and implementation details. Don't add
847 // the bundle type since it is meaningless to the host and just clutters the
848 // output.
849 results.emplace_back(getRelAppIDPathAttrName(), getRelAppIDPathAttr());
850 auto servicePort = getServicePortAttr();
851 results.emplace_back(
852 getServicePortAttrName(),
853 DictionaryAttr::get(
854 getContext(),
855 {
856 NamedAttribute(StringAttr::get(getContext(), "serviceName"),
857 FlatSymbolRefAttr::get(servicePort.getModule())),
858 NamedAttribute(StringAttr::get(getContext(), "port"),
859 servicePort.getName()),
860 }));
861 if (const auto &channelAssignments = getChannelAssignments())
862 results.push_back(
863 NamedAttribute(getChannelAssignmentsAttrName(), *channelAssignments));
864 // Don't add another level for the implementation details.
865 if (const auto &implDetails = getImplDetails())
866 for (const auto &implDetail : *implDetails)
867 results.push_back(implDetail);
868}
869
870StringRef ServiceRequestRecordOp::getManifestClass() { return "clientPort"; }
871
872void ServiceRequestRecordOp::getDetails(
873 SmallVectorImpl<NamedAttribute> &results) {
874 auto *ctxt = getContext();
875 results.emplace_back(StringAttr::get(ctxt, "appID"), getRequestorAttr());
876 results.emplace_back(getTypeIDAttrName(), getTypeIDAttr());
877 auto servicePort = getServicePortAttr();
878 results.emplace_back(
879 getServicePortAttrName(),
880 DictionaryAttr::get(
881 getContext(),
882 {
883 NamedAttribute(StringAttr::get(getContext(), "serviceName"),
884 FlatSymbolRefAttr::get(servicePort.getModule())),
885 NamedAttribute(StringAttr::get(getContext(), "port"),
886 servicePort.getName()),
887 }));
888}
889
890StringRef SymbolMetadataOp::getManifestClass() { return "symInfo"; }
891
892StringRef SymbolConstantsOp::getManifestClass() { return "symConsts"; }
893void SymbolConstantsOp::getDetails(SmallVectorImpl<NamedAttribute> &results) {
894 for (auto &attr : getConstantsAttr())
895 results.push_back(attr);
896}
897
898#define GET_OP_CLASSES
899#include "circt/Dialect/ESI/ESI.cpp.inc"
900
901#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:834
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:622
static ParseResult parseUnPackBundleType(OpAsmParser &parser, SmallVectorImpl< Type > &toChannelTypes, SmallVectorImpl< Type > &fromChannelTypes, Type &type)
Definition ESIOps.cpp:603
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:826
static InstancePath empty
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition CalyxOps.cpp:56
mlir::Type innerType(mlir::Type type)
Definition ESITypes.cpp:423
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:193
Describes a service port.
Definition ESIOps.h:38
This holds the name, type, direction of a module's ports.