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());
71 ParseResult PipelineStageOp::parse(OpAsmParser &parser,
72 OperationState &result) {
73 llvm::SMLoc inputOperandsLoc = parser.getCurrentLocation();
75 SmallVector<OpAsmParser::UnresolvedOperand, 4> operands;
77 if (parser.parseOperandList(operands, 3) ||
78 parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
79 parser.parseType(innerOutputType))
83 result.addTypes({type});
87 if (parser.resolveOperands(operands, {clkTy, i1, type}, inputOperandsLoc,
93 void PipelineStageOp::print(OpAsmPrinter &p) {
94 p <<
" " << getClk() <<
", " << getRst() <<
", " << getInput();
95 p.printOptionalAttrDict((*this)->getAttrs());
99 circt::esi::ChannelType PipelineStageOp::channelType() {
100 return cast<circt::esi::ChannelType>(getInput().getType());
107 ParseResult WrapValidReadyOp::parse(OpAsmParser &parser,
108 OperationState &result) {
109 llvm::SMLoc inputOperandsLoc = parser.getCurrentLocation();
111 llvm::SmallVector<OpAsmParser::UnresolvedOperand, 2> opList;
112 Type innerOutputType;
113 if (parser.parseOperandList(opList, 2, OpAsmParser::Delimiter::None) ||
114 parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
115 parser.parseType(innerOutputType))
118 auto boolType = parser.getBuilder().getI1Type();
121 result.addTypes({outputType, boolType});
122 if (parser.resolveOperands(opList, {innerOutputType, boolType},
123 inputOperandsLoc, result.operands))
128 void WrapValidReadyOp::print(OpAsmPrinter &p) {
129 p <<
" " << getRawInput() <<
", " << getValid();
130 p.printOptionalAttrDict((*this)->getAttrs());
134 void WrapValidReadyOp::build(OpBuilder &b, OperationState &state, Value data,
137 b.getI1Type(), data, valid);
140 LogicalResult WrapValidReadyOp::verify() {
141 if (getChanOutput().getType().getSignaling() != ChannelSignaling::ValidReady)
142 return emitOpError(
"only supports valid-ready signaling");
146 ParseResult UnwrapValidReadyOp::parse(OpAsmParser &parser,
147 OperationState &result) {
148 llvm::SMLoc inputOperandsLoc = parser.getCurrentLocation();
150 llvm::SmallVector<OpAsmParser::UnresolvedOperand, 2> opList;
152 if (parser.parseOperandList(opList, 2, OpAsmParser::Delimiter::None) ||
153 parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
154 parser.parseType(outputType))
160 auto boolType = parser.getBuilder().getI1Type();
162 result.addTypes({inputType.getInner(), boolType});
163 if (parser.resolveOperands(opList, {inputType, boolType}, inputOperandsLoc,
169 void UnwrapValidReadyOp::print(OpAsmPrinter &p) {
170 p <<
" " << getChanInput() <<
", " << getReady();
171 p.printOptionalAttrDict((*this)->getAttrs());
172 p <<
" : " << getRawOutput().getType();
175 LogicalResult UnwrapValidReadyOp::verify() {
176 if (getChanInput().getType().getSignaling() != ChannelSignaling::ValidReady)
177 return emitOpError(
"only supports valid-ready signaling");
181 circt::esi::ChannelType WrapValidReadyOp::channelType() {
182 return cast<circt::esi::ChannelType>(getChanOutput().getType());
185 void UnwrapValidReadyOp::build(OpBuilder &b, OperationState &state,
186 Value inChan, Value ready) {
187 auto inChanType = cast<ChannelType>(inChan.getType());
188 build(b, state, inChanType.getInner(), b.getI1Type(), inChan, ready);
191 circt::esi::ChannelType UnwrapValidReadyOp::channelType() {
192 return cast<circt::esi::ChannelType>(getChanInput().getType());
195 circt::esi::ChannelType WrapFIFOOp::channelType() {
196 return cast<circt::esi::ChannelType>(getChanOutput().getType());
200 Type &chanInputType) {
201 auto loc = p.getCurrentLocation();
203 if (p.parseType(chType))
205 if (chType.getSignaling() != ChannelSignaling::FIFO0)
206 return p.emitError(loc,
"can only wrap into FIFO type");
207 dataType = chType.getInner();
208 chanInputType = chType;
217 LogicalResult WrapFIFOOp::verify() {
218 if (getChanOutput().getType().getSignaling() != ChannelSignaling::FIFO0)
219 return emitOpError(
"only supports FIFO signaling");
223 circt::esi::ChannelType UnwrapFIFOOp::channelType() {
224 return cast<circt::esi::ChannelType>(getChanInput().getType());
227 LogicalResult UnwrapFIFOOp::verify() {
228 if (getChanInput().getType().getSignaling() != ChannelSignaling::FIFO0)
229 return emitOpError(
"only supports FIFO signaling");
235 ValueRange operands, DictionaryAttr,
236 mlir::OpaqueProperties, mlir::RegionRange,
237 SmallVectorImpl<Type> &inferredResulTypes) {
238 inferredResulTypes.push_back(
239 cast<ChannelType>(operands[0].getType()).getInner());
240 inferredResulTypes.push_back(
248 if (!iface.lookupSymbol<InterfaceSignalOp>(
"valid"))
250 if (!iface.lookupSymbol<InterfaceSignalOp>(
"ready"))
252 auto dataSig = iface.lookupSymbol<InterfaceSignalOp>(
"data");
255 return dataSig.getType();
262 circt::sv::ModportType modportType,
263 ChannelType chanType) {
265 SymbolTable::lookupNearestSymbolFrom<circt::sv::InterfaceModportOp>(
266 op, modportType.getModport());
268 return op->emitError(
"Could not find modport ")
269 << modportType.getModport() <<
" in symbol table.";
270 auto iface = cast<circt::sv::InterfaceOp>(modport->getParentOp());
273 return op->emitOpError(
"Interface is not a valid ESI interface.");
274 if (esiDataType != chanType.getInner())
275 return op->emitOpError(
"Operation specifies ")
276 << chanType <<
" but type inside doesn't match interface data type "
277 << esiDataType <<
".";
281 LogicalResult WrapSVInterfaceOp::verify() {
282 auto modportType = cast<circt::sv::ModportType>(getInterfaceSink().getType());
283 auto chanType = cast<ChannelType>(getOutput().getType());
287 circt::esi::ChannelType WrapSVInterfaceOp::channelType() {
288 return cast<circt::esi::ChannelType>(getOutput().getType());
291 LogicalResult UnwrapSVInterfaceOp::verify() {
293 cast<circt::sv::ModportType>(getInterfaceSource().getType());
294 auto chanType = cast<ChannelType>(getChanInput().getType());
298 circt::esi::ChannelType UnwrapSVInterfaceOp::channelType() {
299 return cast<circt::esi::ChannelType>(getChanInput().getType());
302 LogicalResult WrapWindow::verify() {
303 hw::UnionType expectedInput = getWindow().getType().getLoweredType();
304 if (expectedInput == getFrame().getType())
306 return emitOpError(
"Expected input type is ") << expectedInput;
311 ValueRange operands, DictionaryAttr,
312 mlir::OpaqueProperties, mlir::RegionRange,
313 SmallVectorImpl<Type> &inferredReturnTypes) {
314 auto windowType = cast<WindowType>(operands.front().getType());
315 inferredReturnTypes.push_back(windowType.getLoweredType());
322 if (p.parseType(window))
325 frame = window.getLoweredType();
341 hw::InnerRefAttr servicePort) {
342 ModuleOp top = op->getParentOfType<mlir::ModuleOp>();
343 SymbolTable &topSyms = symbolTable.getSymbolTable(top);
345 StringAttr modName = servicePort.getModule();
346 auto serviceDecl = topSyms.lookup<ServiceDeclOpInterface>(modName);
348 return op->emitOpError(
"Could not find service declaration ")
349 << servicePort.getModuleRef();
356 hw::InnerRefAttr servicePort) {
358 if (failed(serviceDecl))
360 auto portInfo = serviceDecl->getPortInfo(servicePort.getName());
361 if (failed(portInfo))
362 return op->emitOpError(
"Could not locate port ") << servicePort.getName();
369 ChannelBundleType svcBundleType,
370 ChannelBundleType reqBundleType,
371 bool skipDirectionCheck) {
372 auto *
ctxt = svcBundleType.getContext();
375 if (svcBundleType.getChannels().size() != reqBundleType.getChannels().size())
376 return req->emitOpError(
377 "Request port bundle channel count does not match service "
378 "port bundle channel count");
381 DenseMap<StringAttr, BundledChannel> declBundleChannels;
383 declBundleChannels[bc.name] = bc;
387 auto f = declBundleChannels.find(bc.name);
388 if (f == declBundleChannels.end())
389 return req->emitOpError(
390 "Request channel name not found in service port bundle");
391 if (!skipDirectionCheck && f->second.direction != bc.direction)
392 return req->emitOpError(
393 "Request channel direction does not match service "
394 "port bundle channel direction");
396 if (f->second.type != bc.type && f->second.type != anyChannelType)
397 return req->emitOpError(
398 "Request channel type does not match service port "
399 "bundle channel type");
405 RequestConnectionOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
409 return checkTypeMatch(*
this, svcPort->type, getToClient().getType(),
false);
412 LogicalResult ServiceImplementConnReqOp::verifySymbolUses(
413 SymbolTableCollection &symbolTable) {
417 return checkTypeMatch(*
this, svcPort->type, getToClient().getType(),
true);
421 for (
auto toClient : getOps<ServiceDeclPortOp>())
424 toClient.getToClientType()});
433 SmallVectorImpl<Type> &toChannelTypes,
434 SmallVectorImpl<Type> &fromChannelTypes, Type &type) {
436 ChannelBundleType bundleType;
437 if (parser.parseType(bundleType))
442 if (ch.direction == ChannelDirection::to)
443 toChannelTypes.push_back(ch.type);
444 else if (ch.direction == ChannelDirection::from)
445 fromChannelTypes.push_back(ch.type);
447 assert(
false &&
"Channel direction invalid");
450 template <
typename T3,
typename T4>
453 p.printType(bundleType);
455 void UnpackBundleOp::build(::mlir::OpBuilder &odsBuilder,
456 ::mlir::OperationState &odsState, Value bundle,
457 mlir::ValueRange fromChannels) {
459 cast<ChannelBundleType>(bundle.getType()).getChannels())
460 if (ch.direction == ChannelDirection::to)
461 odsState.addTypes(ch.type);
462 odsState.addOperands(bundle);
463 odsState.addOperands(fromChannels);
466 LogicalResult PackBundleOp::verify() {
467 if (!getBundle().hasOneUse())
468 return emitOpError(
"bundles must have exactly one user");
471 void PackBundleOp::build(::mlir::OpBuilder &odsBuilder,
472 ::mlir::OperationState &odsState,
473 ChannelBundleType bundleType,
474 mlir::ValueRange toChannels) {
475 odsState.addTypes(bundleType);
476 for (
BundledChannel ch : cast<ChannelBundleType>(bundleType).getChannels())
477 if (ch.direction == ChannelDirection::from)
478 odsState.addTypes(ch.type);
479 odsState.addOperands(toChannels);
482 LogicalResult UnpackBundleOp::verify() {
483 if (!getBundle().hasOneUse())
484 return emitOpError(
"bundles must have exactly one user");
489 if (getNumResults() == 0)
491 setNameFn(getResult(0),
"bundle");
492 for (
auto [idx, from] : llvm::enumerate(llvm::make_filter_range(
494 return ch.
direction == ChannelDirection::from;
496 if (idx + 1 < getNumResults())
497 setNameFn(getResult(idx + 1), from.name.getValue());
501 for (
auto [idx, to] : llvm::enumerate(llvm::make_filter_range(
503 return ch.
direction == ChannelDirection::to;
505 if (idx < getNumResults())
506 setNameFn(getResult(idx), to.name.getValue());
512 LogicalResult ESIPureModuleOp::verify() {
513 ESIDialect *esiDialect = getContext()->getLoadedDialect<ESIDialect>();
514 Block &body = getBody().front();
515 auto channelOrOutput = [](Value v) {
516 if (isa<ChannelType, ChannelBundleType>(v.getType()))
518 if (v.getUsers().empty())
520 return llvm::all_of(v.getUsers(), [](Operation *op) {
521 return isa<ESIPureModuleOutputOp>(op);
525 DenseMap<StringAttr, std::tuple<hw::ModulePort::Direction, Type, Operation *>>
527 for (Operation &op : body.getOperations()) {
528 if (igraph::InstanceOpInterface inst =
529 dyn_cast<igraph::InstanceOpInterface>(op)) {
530 if (llvm::any_of(op.getOperands(), [](Value v) {
531 return !(isa<ChannelType, ChannelBundleType>(v.getType()) ||
532 isa<ESIPureModuleInputOp>(v.getDefiningOp()));
534 return inst.emitOpError(
535 "instances in ESI pure modules can only contain channel ports or "
536 "ports driven by 'input' ops");
537 if (!llvm::all_of(op.getResults(), channelOrOutput))
538 return inst.emitOpError(
539 "instances in ESI pure modules can only contain channel ports or "
540 "drive only 'outputs'");
543 if (op.getDialect() != esiDialect)
544 return op.emitOpError(
"operation not allowed in ESI pure modules");
548 if (
auto port = dyn_cast<ESIPureModuleInputOp>(op)) {
549 auto existing = ports.find(port.getNameAttr());
550 Type portType = port.getResult().getType();
551 if (existing != ports.end()) {
552 auto [dir, type, op] = existing->getSecond();
554 return (port.emitOpError(
"port '")
555 << port.getName() <<
"' previously declared as type " << type)
556 .attachNote(op->getLoc());
558 ports[port.getNameAttr()] = std::make_tuple(
560 }
else if (
auto port = dyn_cast<ESIPureModuleOutputOp>(op)) {
561 auto existing = ports.find(port.getNameAttr());
562 if (existing != ports.end())
563 return (port.emitOpError(
"port '")
564 << port.getName() <<
"' previously declared")
565 .attachNote(std::get<2>(existing->getSecond())->getLoc());
566 ports[port.getNameAttr()] =
568 port.getValue().getType(), port.getOperation());
574 hw::ModuleType ESIPureModuleOp::getHWModuleType() {
580 ::llvm::report_fatal_error(
"not supported");
584 size_t ESIPureModuleOp::getNumInputPorts() {
return 0; }
585 size_t ESIPureModuleOp::getNumOutputPorts() {
return 0; }
586 size_t ESIPureModuleOp::getPortIdForInputId(
size_t) {
587 assert(0 &&
"Out of bounds input port id");
590 size_t ESIPureModuleOp::getPortIdForOutputId(
size_t) {
591 assert(0 &&
"Out of bounds output port id");
596 SmallVector<Location> retval;
600 void ESIPureModuleOp::setAllPortLocsAttrs(ArrayRef<Attribute> locs) {
601 emitError(
"No ports for port locations");
605 emitError(
"No ports for port naming");
608 void ESIPureModuleOp::setAllPortAttrs(ArrayRef<Attribute> attrs) {
609 emitError(
"No ports for port attributes");
612 void ESIPureModuleOp::removeAllPortAttrs() {
613 emitError(
"No ports for port attributes)");
616 ArrayRef<Attribute> ESIPureModuleOp::getAllPortAttrs() {
return {}; }
619 emitError(
"No ports for port types");
626 StringRef ServiceImplRecordOp::getManifestClass() {
return "service"; }
628 void ServiceImplRecordOp::getDetails(SmallVectorImpl<NamedAttribute> &results) {
629 auto *
ctxt = getContext();
631 results.emplace_back(getAppIDAttrName(), getAppIDAttr());
633 results.emplace_back(getServiceAttrName(), getServiceAttr());
634 results.emplace_back(getServiceImplNameAttrName(), getServiceImplNameAttr());
636 for (
auto implDetail : getImplDetailsAttr().getValue())
637 results.push_back(implDetail);
640 SmallVector<Attribute, 8> reqDetails;
641 for (
auto reqDetail : getReqDetails().front().getOps<IsManifestData>())
642 reqDetails.push_back(reqDetail.getDetailsAsDict());
648 Region &reqDetailsRegion) {
649 parser.parseOptionalRegion(reqDetailsRegion);
650 if (reqDetailsRegion.empty())
651 reqDetailsRegion.emplaceBlock();
656 Region &reqDetailsRegion) {
657 if (!reqDetailsRegion.empty() && !reqDetailsRegion.front().empty())
658 p.printRegion(reqDetailsRegion,
false,
662 StringRef ServiceImplClientRecordOp::getManifestClass() {
663 return "service_client";
665 void ServiceImplClientRecordOp::getDetails(
666 SmallVectorImpl<NamedAttribute> &results) {
670 results.emplace_back(getRelAppIDPathAttrName(), getRelAppIDPathAttr());
671 results.emplace_back(getServicePortAttrName(), getServicePortAttr());
673 for (
auto implDetail : getImplDetailsAttr().getValue())
674 results.push_back(implDetail);
677 StringRef ServiceRequestRecordOp::getManifestClass() {
return "client_port"; }
679 void ServiceRequestRecordOp::getDetails(
680 SmallVectorImpl<NamedAttribute> &results) {
681 auto *
ctxt = getContext();
683 results.emplace_back(getBundleTypeAttrName(), getBundleTypeAttr());
684 results.emplace_back(getServicePortAttrName(), getServicePortAttr());
685 if (
auto stdSvc = getStdServiceAttr())
686 results.emplace_back(getStdServiceAttrName(), getStdServiceAttr());
689 StringRef SymbolMetadataOp::getManifestClass() {
return "sym_info"; }
691 #define GET_OP_CLASSES
692 #include "circt/Dialect/ESI/ESI.cpp.inc"
694 #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...
static LogicalResult checkTypeMatch(Operation *req, ChannelBundleType svcBundleType, ChannelBundleType reqBundleType, bool skipDirectionCheck)
Check that the channels on two bundles match allowing for AnyType in the 'svc' bundle.
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)
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
mlir::Type innerType(mlir::Type type)
LogicalResult inferReturnTypes(MLIRContext *context, std::optional< Location > loc, ValueRange operands, DictionaryAttr attrs, mlir::OpaqueProperties properties, mlir::RegionRange regions, SmallVectorImpl< Type > &results, llvm::function_ref< FIRRTLType(ValueRange, ArrayRef< NamedAttribute >, std::optional< Location >)> callback)
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.