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");
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;
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();
377 ChannelBundleType svcBundleType,
378 ChannelBundleType reqBundleType,
379 bool skipDirectionCheck) {
380 auto *
ctxt = svcBundleType.getContext();
383 if (svcBundleType.getChannels().size() != reqBundleType.getChannels().size())
384 return req->emitOpError(
385 "Request port bundle channel count does not match service "
386 "port bundle channel count");
389 DenseMap<StringAttr, BundledChannel> declBundleChannels;
391 declBundleChannels[bc.name] = bc;
395 auto f = declBundleChannels.find(bc.name);
396 if (f == declBundleChannels.end())
397 return req->emitOpError(
398 "Request channel name not found in service port bundle");
399 if (!skipDirectionCheck && f->second.direction != bc.direction)
400 return req->emitOpError(
401 "Request channel direction does not match service "
402 "port bundle channel direction");
404 if (f->second.type != bc.type && f->second.type != anyChannelType)
405 return req->emitOpError(
406 "Request channel type does not match service port "
407 "bundle channel type");
413 RequestConnectionOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
417 return checkTypeMatch(*
this, svcPort->type, getToClient().getType(),
false);
420 LogicalResult ServiceImplementConnReqOp::verifySymbolUses(
421 SymbolTableCollection &symbolTable) {
425 return checkTypeMatch(*
this, svcPort->type, getToClient().getType(),
true);
429 for (
auto toClient : getOps<ServiceDeclPortOp>())
432 toClient.getToClientType()});
441 SmallVectorImpl<Type> &toChannelTypes,
442 SmallVectorImpl<Type> &fromChannelTypes, Type &type) {
444 ChannelBundleType bundleType;
445 if (parser.parseType(bundleType))
450 if (ch.direction == ChannelDirection::to)
451 toChannelTypes.push_back(ch.type);
452 else if (ch.direction == ChannelDirection::from)
453 fromChannelTypes.push_back(ch.type);
455 assert(
false &&
"Channel direction invalid");
458 template <
typename T3,
typename T4>
461 p.printType(bundleType);
463 void UnpackBundleOp::build(::mlir::OpBuilder &odsBuilder,
464 ::mlir::OperationState &odsState, Value bundle,
465 mlir::ValueRange fromChannels) {
467 cast<ChannelBundleType>(bundle.getType()).getChannels())
468 if (ch.direction == ChannelDirection::to)
469 odsState.addTypes(ch.type);
470 odsState.addOperands(bundle);
471 odsState.addOperands(fromChannels);
475 if (!getBundle().hasOneUse())
476 return emitOpError(
"bundles must have exactly one user");
479 void PackBundleOp::build(::mlir::OpBuilder &odsBuilder,
480 ::mlir::OperationState &odsState,
481 ChannelBundleType bundleType,
482 mlir::ValueRange toChannels) {
483 odsState.addTypes(bundleType);
484 for (
BundledChannel ch : cast<ChannelBundleType>(bundleType).getChannels())
485 if (ch.direction == ChannelDirection::from)
486 odsState.addTypes(ch.type);
487 odsState.addOperands(toChannels);
491 if (!getBundle().hasOneUse())
492 return emitOpError(
"bundles must have exactly one user");
497 if (getNumResults() == 0)
499 setNameFn(getResult(0),
"bundle");
500 for (
auto [idx, from] : llvm::enumerate(llvm::make_filter_range(
502 return ch.
direction == ChannelDirection::from;
504 if (idx + 1 < getNumResults())
505 setNameFn(getResult(idx + 1), from.name.getValue());
509 for (
auto [idx, to] : llvm::enumerate(llvm::make_filter_range(
511 return ch.
direction == ChannelDirection::to;
513 if (idx < getNumResults())
514 setNameFn(getResult(idx), to.name.getValue());
521 ESIDialect *esiDialect = getContext()->getLoadedDialect<ESIDialect>();
522 Block &body = getBody().front();
523 auto channelOrOutput = [](Value v) {
524 if (isa<ChannelType, ChannelBundleType>(v.getType()))
526 if (v.getUsers().empty())
528 return llvm::all_of(v.getUsers(), [](Operation *op) {
529 return isa<ESIPureModuleOutputOp>(op);
533 DenseMap<StringAttr, std::tuple<hw::ModulePort::Direction, Type, Operation *>>
535 for (Operation &op : body.getOperations()) {
536 if (igraph::InstanceOpInterface inst =
537 dyn_cast<igraph::InstanceOpInterface>(op)) {
538 if (llvm::any_of(op.getOperands(), [](Value v) {
539 return !(isa<ChannelType, ChannelBundleType>(v.getType()) ||
540 isa<ESIPureModuleInputOp>(v.getDefiningOp()));
542 return inst.emitOpError(
543 "instances in ESI pure modules can only contain channel ports or "
544 "ports driven by 'input' ops");
545 if (!llvm::all_of(op.getResults(), channelOrOutput))
546 return inst.emitOpError(
547 "instances in ESI pure modules can only contain channel ports or "
548 "drive only 'outputs'");
551 if (op.getDialect() != esiDialect)
552 return op.emitOpError(
"operation not allowed in ESI pure modules");
556 if (
auto port = dyn_cast<ESIPureModuleInputOp>(op)) {
557 auto existing = ports.find(port.getNameAttr());
558 Type portType = port.getResult().getType();
559 if (existing != ports.end()) {
560 auto [dir, type, op] = existing->getSecond();
562 return (port.emitOpError(
"port '")
563 << port.getName() <<
"' previously declared as type " << type)
564 .attachNote(op->getLoc());
566 ports[port.getNameAttr()] = std::make_tuple(
568 }
else if (
auto port = dyn_cast<ESIPureModuleOutputOp>(op)) {
569 auto existing = ports.find(port.getNameAttr());
570 if (existing != ports.end())
571 return (port.emitOpError(
"port '")
572 << port.getName() <<
"' previously declared")
573 .attachNote(std::get<2>(existing->getSecond())->getLoc());
574 ports[port.getNameAttr()] =
576 port.getValue().getType(), port.getOperation());
582 hw::ModuleType ESIPureModuleOp::getHWModuleType() {
588 ::llvm::report_fatal_error(
"not supported");
592 size_t ESIPureModuleOp::getNumInputPorts() {
return 0; }
593 size_t ESIPureModuleOp::getNumOutputPorts() {
return 0; }
594 size_t ESIPureModuleOp::getPortIdForInputId(
size_t) {
595 assert(0 &&
"Out of bounds input port id");
598 size_t ESIPureModuleOp::getPortIdForOutputId(
size_t) {
599 assert(0 &&
"Out of bounds output port id");
604 SmallVector<Location> retval;
608 void ESIPureModuleOp::setAllPortLocsAttrs(ArrayRef<Attribute> locs) {
609 emitError(
"No ports for port locations");
613 emitError(
"No ports for port naming");
616 void ESIPureModuleOp::setAllPortAttrs(ArrayRef<Attribute> attrs) {
617 emitError(
"No ports for port attributes");
620 void ESIPureModuleOp::removeAllPortAttrs() {
621 emitError(
"No ports for port attributes)");
624 ArrayRef<Attribute> ESIPureModuleOp::getAllPortAttrs() {
return {}; }
627 emitError(
"No ports for port types");
634 StringRef ServiceImplRecordOp::getManifestClass() {
return "service"; }
636 void ServiceImplRecordOp::getDetails(SmallVectorImpl<NamedAttribute> &results) {
637 auto *
ctxt = getContext();
639 results.emplace_back(getAppIDAttrName(), getAppIDAttr());
641 results.emplace_back(getServiceAttrName(), getServiceAttr());
642 results.emplace_back(getServiceImplNameAttrName(), getServiceImplNameAttr());
644 for (
auto implDetail : getImplDetailsAttr().getValue())
645 results.push_back(implDetail);
648 SmallVector<Attribute, 8> reqDetails;
649 for (
auto reqDetail : getReqDetails().front().getOps<IsManifestData>())
650 reqDetails.push_back(reqDetail.getDetailsAsDict());
656 Region &reqDetailsRegion) {
657 parser.parseOptionalRegion(reqDetailsRegion);
658 if (reqDetailsRegion.empty())
659 reqDetailsRegion.emplaceBlock();
664 Region &reqDetailsRegion) {
665 if (!reqDetailsRegion.empty() && !reqDetailsRegion.front().empty())
666 p.printRegion(reqDetailsRegion,
false,
670 StringRef ServiceImplClientRecordOp::getManifestClass() {
671 return "service_client";
673 void ServiceImplClientRecordOp::getDetails(
674 SmallVectorImpl<NamedAttribute> &results) {
678 results.emplace_back(getRelAppIDPathAttrName(), getRelAppIDPathAttr());
679 results.emplace_back(getServicePortAttrName(), getServicePortAttr());
681 for (
auto implDetail : getImplDetailsAttr().getValue())
682 results.push_back(implDetail);
685 StringRef ServiceRequestRecordOp::getManifestClass() {
return "client_port"; }
687 void ServiceRequestRecordOp::getDetails(
688 SmallVectorImpl<NamedAttribute> &results) {
689 auto *
ctxt = getContext();
691 results.emplace_back(getBundleTypeAttrName(), getBundleTypeAttr());
692 results.emplace_back(getServicePortAttrName(), getServicePortAttr());
693 if (
auto stdSvc = getStdServiceAttr())
694 results.emplace_back(getStdServiceAttrName(), getStdServiceAttr());
697 StringRef SymbolMetadataOp::getManifestClass() {
return "sym_info"; }
699 #define GET_OP_CLASSES
700 #include "circt/Dialect/ESI/ESI.cpp.inc"
702 #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)
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 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.