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