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