20 #include "mlir/IR/Builders.h"
21 #include "mlir/IR/BuiltinTypes.h"
22 #include "mlir/IR/PatternMatch.h"
23 #include "mlir/IR/SymbolTable.h"
25 using namespace circt;
32 ParseResult ChannelBufferOp::parse(OpAsmParser &parser,
33 OperationState &result) {
34 llvm::SMLoc inputOperandsLoc = parser.getCurrentLocation();
36 llvm::SmallVector<OpAsmParser::UnresolvedOperand, 4> operands;
37 if (parser.parseOperandList(operands, 3,
38 OpAsmParser::Delimiter::None))
42 if (parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
43 parser.parseType(innerOutputType))
47 result.addTypes({outputType});
51 if (parser.resolveOperands(operands, {clkTy, i1, outputType},
52 inputOperandsLoc, result.operands))
57 void ChannelBufferOp::print(OpAsmPrinter &p) {
58 p <<
" " << getClk() <<
", " << getRst() <<
", " << getInput();
59 p.printOptionalAttrDict((*this)->getAttrs());
63 circt::esi::ChannelType ChannelBufferOp::channelType() {
64 return cast<circt::esi::ChannelType>(getInput().getType());
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");
79 ParseResult PipelineStageOp::parse(OpAsmParser &parser,
80 OperationState &result) {
81 llvm::SMLoc inputOperandsLoc = parser.getCurrentLocation();
83 SmallVector<OpAsmParser::UnresolvedOperand, 4> operands;
85 if (parser.parseOperandList(operands, 3) ||
86 parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
87 parser.parseType(innerOutputType))
91 result.addTypes({type});
95 if (parser.resolveOperands(operands, {clkTy, i1, type}, inputOperandsLoc,
101 void PipelineStageOp::print(OpAsmPrinter &p) {
102 p <<
" " << getClk() <<
", " << getRst() <<
", " << getInput();
103 p.printOptionalAttrDict((*this)->getAttrs());
107 circt::esi::ChannelType PipelineStageOp::channelType() {
108 return cast<circt::esi::ChannelType>(getInput().getType());
115 ParseResult WrapValidReadyOp::parse(OpAsmParser &parser,
116 OperationState &result) {
117 llvm::SMLoc inputOperandsLoc = parser.getCurrentLocation();
119 llvm::SmallVector<OpAsmParser::UnresolvedOperand, 2> opList;
120 Type innerOutputType;
121 if (parser.parseOperandList(opList, 2, OpAsmParser::Delimiter::None) ||
122 parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
123 parser.parseType(innerOutputType))
126 auto boolType = parser.getBuilder().getI1Type();
129 result.addTypes({outputType, boolType});
130 if (parser.resolveOperands(opList, {innerOutputType, boolType},
131 inputOperandsLoc, result.operands))
136 void WrapValidReadyOp::print(OpAsmPrinter &p) {
137 p <<
" " << getRawInput() <<
", " << getValid();
138 p.printOptionalAttrDict((*this)->getAttrs());
142 void WrapValidReadyOp::build(OpBuilder &b, OperationState &state, Value data,
145 b.getI1Type(), data, valid);
149 if (getChanOutput().getType().getSignaling() != ChannelSignaling::ValidReady)
150 return emitOpError(
"only supports valid-ready signaling");
154 ParseResult UnwrapValidReadyOp::parse(OpAsmParser &parser,
155 OperationState &result) {
156 llvm::SMLoc inputOperandsLoc = parser.getCurrentLocation();
158 llvm::SmallVector<OpAsmParser::UnresolvedOperand, 2> opList;
160 if (parser.parseOperandList(opList, 2, OpAsmParser::Delimiter::None) ||
161 parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
162 parser.parseType(outputType))
168 auto boolType = parser.getBuilder().getI1Type();
170 result.addTypes({inputType.getInner(), boolType});
171 if (parser.resolveOperands(opList, {inputType, boolType}, inputOperandsLoc,
177 void UnwrapValidReadyOp::print(OpAsmPrinter &p) {
178 p <<
" " << getChanInput() <<
", " << getReady();
179 p.printOptionalAttrDict((*this)->getAttrs());
180 p <<
" : " << getRawOutput().getType();
184 if (getChanInput().getType().getSignaling() != ChannelSignaling::ValidReady)
185 return emitOpError(
"only supports valid-ready signaling");
189 circt::esi::ChannelType WrapValidReadyOp::channelType() {
190 return cast<circt::esi::ChannelType>(getChanOutput().getType());
193 void UnwrapValidReadyOp::build(OpBuilder &b, OperationState &state,
194 Value inChan, Value ready) {
195 auto inChanType = cast<ChannelType>(inChan.getType());
196 build(b, state, inChanType.getInner(), b.getI1Type(), inChan, ready);
199 circt::esi::ChannelType UnwrapValidReadyOp::channelType() {
200 return cast<circt::esi::ChannelType>(getChanInput().getType());
203 circt::esi::ChannelType WrapFIFOOp::channelType() {
204 return cast<circt::esi::ChannelType>(getChanOutput().getType());
208 Type &chanInputType) {
209 auto loc = p.getCurrentLocation();
211 if (p.parseType(chType))
213 if (chType.getSignaling() != ChannelSignaling::FIFO)
214 return p.emitError(loc,
"can only wrap into FIFO type");
215 dataType = chType.getInner();
216 chanInputType = chType;
226 if (getChanOutput().getType().getSignaling() != ChannelSignaling::FIFO)
227 return emitOpError(
"only supports FIFO signaling");
231 circt::esi::ChannelType UnwrapFIFOOp::channelType() {
232 return cast<circt::esi::ChannelType>(getChanInput().getType());
236 if (getChanInput().getType().getSignaling() != ChannelSignaling::FIFO)
237 return emitOpError(
"only supports FIFO signaling");
242 UnwrapFIFOOp::inferReturnTypes(MLIRContext *context, std::optional<Location>,
243 ValueRange operands, DictionaryAttr,
244 mlir::OpaqueProperties, mlir::RegionRange,
245 SmallVectorImpl<Type> &inferredResulTypes) {
246 inferredResulTypes.push_back(
247 cast<ChannelType>(operands[0].getType()).getInner());
248 inferredResulTypes.push_back(
256 if (!iface.lookupSymbol<InterfaceSignalOp>(
"valid"))
258 if (!iface.lookupSymbol<InterfaceSignalOp>(
"ready"))
260 auto dataSig = iface.lookupSymbol<InterfaceSignalOp>(
"data");
263 return dataSig.getType();
270 circt::sv::ModportType modportType,
271 ChannelType chanType) {
273 SymbolTable::lookupNearestSymbolFrom<circt::sv::InterfaceModportOp>(
274 op, modportType.getModport());
276 return op->emitError(
"Could not find modport ")
277 << modportType.getModport() <<
" in symbol table.";
278 auto iface = cast<circt::sv::InterfaceOp>(modport->getParentOp());
281 return op->emitOpError(
"Interface is not a valid ESI interface.");
282 if (esiDataType != chanType.getInner())
283 return op->emitOpError(
"Operation specifies ")
284 << chanType <<
" but type inside doesn't match interface data type "
285 << esiDataType <<
".";
290 auto modportType = cast<circt::sv::ModportType>(getInterfaceSink().getType());
291 auto chanType = cast<ChannelType>(getOutput().getType());
295 circt::esi::ChannelType WrapSVInterfaceOp::channelType() {
296 return cast<circt::esi::ChannelType>(getOutput().getType());
301 cast<circt::sv::ModportType>(getInterfaceSource().getType());
302 auto chanType = cast<ChannelType>(getChanInput().getType());
306 circt::esi::ChannelType UnwrapSVInterfaceOp::channelType() {
307 return cast<circt::esi::ChannelType>(getChanInput().getType());
311 hw::UnionType expectedInput = getWindow().getType().getLoweredType();
312 if (expectedInput == getFrame().getType())
314 return emitOpError(
"Expected input type is ") << expectedInput;
318 UnwrapWindow::inferReturnTypes(MLIRContext *, std::optional<Location>,
319 ValueRange operands, DictionaryAttr,
320 mlir::OpaqueProperties, mlir::RegionRange,
321 SmallVectorImpl<Type> &inferredReturnTypes) {
322 auto windowType = cast<WindowType>(operands.front().getType());
323 inferredReturnTypes.push_back(windowType.getLoweredType());
330 if (p.parseType(window))
333 frame = window.getLoweredType();
347 static FailureOr<ServiceDeclOpInterface>
349 hw::InnerRefAttr servicePort) {
350 ModuleOp top = op->getParentOfType<mlir::ModuleOp>();
351 SymbolTable &topSyms = symbolTable.getSymbolTable(top);
353 StringAttr modName = servicePort.getModule();
354 auto serviceDecl = topSyms.lookup<ServiceDeclOpInterface>(modName);
356 return op->emitOpError(
"Could not find service declaration ")
357 << servicePort.getModuleRef();
362 static FailureOr<ServicePortInfo>
364 hw::InnerRefAttr servicePort) {
366 if (failed(serviceDecl))
368 auto portInfo = serviceDecl->getPortInfo(servicePort.getName());
369 if (failed(portInfo))
370 return op->emitOpError(
"Could not locate port ") << servicePort.getName();
379 if (expected == actual)
383 return TypeSwitch<Type, LogicalResult>(expected)
385 .Case<AnyType>([&](Type) {
return success(); })
387 .Case<ChannelType>([&](ChannelType expectedChannel) {
388 auto actualChannel = dyn_cast<ChannelType>(actual);
392 actualChannel.getInner());
395 .Case<hw::StructType>([&](hw::StructType expectedStruct) {
396 auto actualStruct = dyn_cast<hw::StructType>(actual);
399 auto expectedFields = expectedStruct.getElements();
400 auto actualFields = actualStruct.getElements();
401 if (expectedFields.size() != actualFields.size())
403 for (
auto [efield, afield] : llvm::zip(expectedFields, actualFields)) {
404 if (efield.name != afield.name)
412 .Case<hw::ArrayType>([&](hw::ArrayType expectedArray) {
413 auto actualArray = dyn_cast<hw::ArrayType>(actual);
416 if (expectedArray.getNumElements() != actualArray.getNumElements())
419 actualArray.getElementType());
422 .Case<hw::UnionType>([&](hw::UnionType expectedUnion) {
423 auto actualUnion = dyn_cast<hw::UnionType>(actual);
426 auto expectedElements = expectedUnion.getElements();
427 auto actualElements = actualUnion.getElements();
428 if (expectedElements.size() != actualElements.size())
430 for (
auto [efield, afield] :
431 llvm::zip(expectedElements, actualElements)) {
432 if (efield.name != afield.name)
434 if (efield.offset != afield.offset)
442 .Case<ListType>([&](ListType expectedList) {
443 auto actualList = dyn_cast<ListType>(actual);
447 actualList.getElementType());
450 .Case<WindowType>([&](WindowType expectedWindow) {
451 auto actualWindow = dyn_cast<WindowType>(actual);
455 actualWindow.getInto());
458 .Case<hw::TypeAliasType>([&](hw::TypeAliasType expectedAlias) {
459 auto actualAlias = dyn_cast<hw::TypeAliasType>(actual);
463 actualAlias.getCanonicalType());
466 .Default([&](Type) {
return failure(); });
472 ChannelBundleType svcBundleType,
473 ChannelBundleType reqBundleType,
474 bool skipDirectionCheck) {
475 if (svcBundleType.getChannels().size() != reqBundleType.getChannels().size())
476 return req->emitOpError(
477 "Request port bundle channel count does not match service "
478 "port bundle channel count");
481 DenseMap<StringAttr, BundledChannel> declBundleChannels;
483 declBundleChannels[bc.name] = bc;
487 auto f = declBundleChannels.find(bc.name);
488 if (f == declBundleChannels.end())
489 return req->emitOpError(
490 "Request channel name not found in service port bundle");
491 if (!skipDirectionCheck && f->second.direction != bc.direction)
492 return req->emitOpError(
493 "Request channel direction does not match service "
494 "port bundle channel direction");
497 return req->emitOpError(
498 "Request channel type does not match service port "
499 "bundle channel type")
501 <<
"Service port '" << bc.name.getValue()
502 <<
"' type: " << f->second.type;
511 RequestConnectionOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
519 LogicalResult ServiceImplementConnReqOp::verifySymbolUses(
520 SymbolTableCollection &symbolTable) {
529 for (
auto toClient : getOps<ServiceDeclPortOp>())
532 toClient.getToClientType()});
541 SmallVectorImpl<Type> &toChannelTypes,
542 SmallVectorImpl<Type> &fromChannelTypes, Type &type) {
544 ChannelBundleType bundleType;
545 if (parser.parseType(bundleType))
550 if (ch.direction == ChannelDirection::to)
551 toChannelTypes.push_back(ch.type);
552 else if (ch.direction == ChannelDirection::from)
553 fromChannelTypes.push_back(ch.type);
555 assert(
false &&
"Channel direction invalid");
558 template <
typename T3,
typename T4>
561 p.printType(bundleType);
563 void UnpackBundleOp::build(::mlir::OpBuilder &odsBuilder,
564 ::mlir::OperationState &odsState, Value bundle,
565 mlir::ValueRange fromChannels) {
567 cast<ChannelBundleType>(bundle.getType()).getChannels())
568 if (ch.direction == ChannelDirection::to)
569 odsState.addTypes(ch.type);
570 odsState.addOperands(bundle);
571 odsState.addOperands(fromChannels);
575 if (!getBundle().hasOneUse())
576 return emitOpError(
"bundles must have exactly one user");
579 void PackBundleOp::build(::mlir::OpBuilder &odsBuilder,
580 ::mlir::OperationState &odsState,
581 ChannelBundleType bundleType,
582 mlir::ValueRange toChannels) {
583 odsState.addTypes(bundleType);
584 for (
BundledChannel ch : cast<ChannelBundleType>(bundleType).getChannels())
585 if (ch.direction == ChannelDirection::from)
586 odsState.addTypes(ch.type);
587 odsState.addOperands(toChannels);
591 if (!getBundle().hasOneUse())
592 return emitOpError(
"bundles must have exactly one user");
597 if (getNumResults() == 0)
599 setNameFn(getResult(0),
"bundle");
600 for (
auto [idx, from] : llvm::enumerate(llvm::make_filter_range(
602 return ch.
direction == ChannelDirection::from;
604 if (idx + 1 < getNumResults())
605 setNameFn(getResult(idx + 1), from.name.getValue());
609 for (
auto [idx, to] : llvm::enumerate(llvm::make_filter_range(
611 return ch.
direction == ChannelDirection::to;
613 if (idx < getNumResults())
614 setNameFn(getResult(idx), to.name.getValue());
621 ESIDialect *esiDialect = getContext()->getLoadedDialect<ESIDialect>();
622 Block &body = getBody().front();
623 auto channelOrOutput = [](Value v) {
624 if (isa<ChannelType, ChannelBundleType>(v.getType()))
626 if (v.getUsers().empty())
628 return llvm::all_of(v.getUsers(), [](Operation *op) {
629 return isa<ESIPureModuleOutputOp>(op);
633 DenseMap<StringAttr, std::tuple<hw::ModulePort::Direction, Type, Operation *>>
635 for (Operation &op : body.getOperations()) {
636 if (igraph::InstanceOpInterface inst =
637 dyn_cast<igraph::InstanceOpInterface>(op)) {
638 if (llvm::any_of(op.getOperands(), [](Value v) {
639 return !(isa<ChannelType, ChannelBundleType>(v.getType()) ||
640 isa<ESIPureModuleInputOp>(v.getDefiningOp()));
642 return inst.emitOpError(
643 "instances in ESI pure modules can only contain channel ports or "
644 "ports driven by 'input' ops");
645 if (!llvm::all_of(op.getResults(), channelOrOutput))
646 return inst.emitOpError(
647 "instances in ESI pure modules can only contain channel ports or "
648 "drive only 'outputs'");
651 if (op.getDialect() != esiDialect)
652 return op.emitOpError(
"operation not allowed in ESI pure modules");
656 if (
auto port = dyn_cast<ESIPureModuleInputOp>(op)) {
657 auto existing = ports.find(port.getNameAttr());
658 Type portType = port.getResult().getType();
659 if (existing != ports.end()) {
660 auto [dir, type, op] = existing->getSecond();
662 return (port.emitOpError(
"port '")
663 << port.getName() <<
"' previously declared as type " << type)
664 .attachNote(op->getLoc());
666 ports[port.getNameAttr()] = std::make_tuple(
668 }
else if (
auto port = dyn_cast<ESIPureModuleOutputOp>(op)) {
669 auto existing = ports.find(port.getNameAttr());
670 if (existing != ports.end())
671 return (port.emitOpError(
"port '")
672 << port.getName() <<
"' previously declared")
673 .attachNote(std::get<2>(existing->getSecond())->getLoc());
674 ports[port.getNameAttr()] =
676 port.getValue().getType(), port.getOperation());
682 hw::ModuleType ESIPureModuleOp::getHWModuleType() {
688 ::llvm::report_fatal_error(
"not supported");
692 size_t ESIPureModuleOp::getNumInputPorts() {
return 0; }
693 size_t ESIPureModuleOp::getNumOutputPorts() {
return 0; }
694 size_t ESIPureModuleOp::getPortIdForInputId(
size_t) {
695 assert(0 &&
"Out of bounds input port id");
698 size_t ESIPureModuleOp::getPortIdForOutputId(
size_t) {
699 assert(0 &&
"Out of bounds output port id");
704 SmallVector<Location> retval;
708 void ESIPureModuleOp::setAllPortLocsAttrs(ArrayRef<Attribute> locs) {
709 emitError(
"No ports for port locations");
713 emitError(
"No ports for port naming");
716 void ESIPureModuleOp::setAllPortAttrs(ArrayRef<Attribute> attrs) {
717 emitError(
"No ports for port attributes");
720 void ESIPureModuleOp::removeAllPortAttrs() {
721 emitError(
"No ports for port attributes)");
724 ArrayRef<Attribute> ESIPureModuleOp::getAllPortAttrs() {
return {}; }
727 emitError(
"No ports for port types");
734 StringRef ServiceImplRecordOp::getManifestClass() {
return "service"; }
736 void ServiceImplRecordOp::getDetails(SmallVectorImpl<NamedAttribute> &results) {
737 auto *
ctxt = getContext();
739 results.emplace_back(getAppIDAttrName(), getAppIDAttr());
741 results.emplace_back(getServiceAttrName(), getServiceAttr());
742 results.emplace_back(getServiceImplNameAttrName(), getServiceImplNameAttr());
744 for (
auto implDetail : getImplDetailsAttr().getValue())
745 results.push_back(implDetail);
748 SmallVector<Attribute, 8> reqDetails;
749 for (
auto reqDetail : getReqDetails().front().getOps<IsManifestData>())
750 reqDetails.push_back(reqDetail.getDetailsAsDict());
756 Region &reqDetailsRegion) {
757 parser.parseOptionalRegion(reqDetailsRegion);
758 if (reqDetailsRegion.empty())
759 reqDetailsRegion.emplaceBlock();
764 Region &reqDetailsRegion) {
765 if (!reqDetailsRegion.empty() && !reqDetailsRegion.front().empty())
766 p.printRegion(reqDetailsRegion,
false,
770 StringRef ServiceImplClientRecordOp::getManifestClass() {
771 return "serviceClient";
773 void ServiceImplClientRecordOp::getDetails(
774 SmallVectorImpl<NamedAttribute> &results) {
778 results.emplace_back(getRelAppIDPathAttrName(), getRelAppIDPathAttr());
779 auto servicePort = getServicePortAttr();
780 results.emplace_back(
781 getServicePortAttrName(),
788 servicePort.getName()),
790 if (
const auto &channelAssignments = getChannelAssignments())
792 NamedAttribute(getChannelAssignmentsAttrName(), *channelAssignments));
794 if (
const auto &implDetails = getImplDetails())
795 for (
const auto &implDetail : *implDetails)
796 results.push_back(implDetail);
799 StringRef ServiceRequestRecordOp::getManifestClass() {
return "clientPort"; }
801 void ServiceRequestRecordOp::getDetails(
802 SmallVectorImpl<NamedAttribute> &results) {
803 auto *
ctxt = getContext();
805 results.emplace_back(getTypeIDAttrName(), getTypeIDAttr());
806 auto servicePort = getServicePortAttr();
807 results.emplace_back(
808 getServicePortAttrName(),
815 servicePort.getName()),
819 StringRef SymbolMetadataOp::getManifestClass() {
return "symInfo"; }
821 StringRef SymbolConstantsOp::getManifestClass() {
return "symConsts"; }
822 void SymbolConstantsOp::getDetails(SmallVectorImpl<NamedAttribute> &results) {
823 for (
auto &attr : getConstantsAttr())
824 results.push_back(attr);
827 #define GET_OP_CLASSES
828 #include "circt/Dialect/ESI/ESI.cpp.inc"
830 #include "circt/Dialect/ESI/ESIInterfaces.cpp.inc"
assert(baseType &&"element must be base type")
return wrap(CMemoryType::get(unwrap(ctx), baseType, numElements))
static bool parseInferWindowRet(OpAsmParser &p, Type &frame, Type &windowOut)
Determine the input type ('frame') from the return type ('window').
ParseResult parseWrapFIFOType(OpAsmParser &p, Type &dataType, Type &chanInputType)
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...
void printServiceImplRecordReqDetails(OpAsmPrinter &p, ServiceImplRecordOp, Region &reqDetailsRegion)
static FailureOr< ServicePortInfo > getServicePortInfo(Operation *op, SymbolTableCollection &symbolTable, hw::InnerRefAttr servicePort)
Get the port info for the specified service decl and port name.
static Type getEsiDataType(circt::sv::InterfaceOp iface)
If 'iface' looks like an ESI interface, return the inner data type.
static FailureOr< ServiceDeclOpInterface > getServiceDecl(Operation *op, SymbolTableCollection &symbolTable, hw::InnerRefAttr servicePort)
Get the port declaration op for the specified service decl, port name.
static void printUnPackBundleType(OpAsmPrinter &p, Operation *, T3, T4, Type bundleType)
static ParseResult parseUnPackBundleType(OpAsmParser &parser, SmallVectorImpl< Type > &toChannelTypes, SmallVectorImpl< Type > &fromChannelTypes, Type &type)
static void printInferWindowRet(OpAsmPrinter &p, Operation *, Type, Type window)
void printWrapFIFOType(OpAsmPrinter &p, WrapFIFOOp wrap, Type dataType, Type chanType)
bool parseServiceImplRecordReqDetails(OpAsmParser &parser, Region &reqDetailsRegion)
static SmallVector< Location > getAllPortLocs(ModTy module)
static void setAllPortNames(ArrayRef< Attribute > names, ModTy module)
static void setHWModuleType(ModTy &mod, ModuleType type)
static SmallVector< PortInfo > getPortList(ModuleTy &mod)
static PortInfo getPort(ModuleTy &mod, size_t idx)
static LogicalResult verify(Value clock, bool eventExists, mlir::Location loc)
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
mlir::Type innerType(mlir::Type type)
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.
LogicalResult checkInnerTypeMatch(Type expected, Type actual)
size_t getNumPorts(Operation *op)
Return the number of ports in a module-like thing (modules, memories, etc)
void getAsmResultNames(OpAsmSetValueNameFn setNameFn, StringRef instanceName, ArrayAttr resultNames, ValueRange results)
Suggest a name for each result value based on the saved result names attribute.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
function_ref< void(Value, StringRef)> OpAsmSetValueNameFn
ChannelDirection direction
Describes a service port.
This holds the name, type, direction of a module's ports.