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