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 getInput().getType().cast<circt::esi::ChannelType>();
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 getInput().getType().cast<circt::esi::ChannelType>();
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 getChanOutput().getType().cast<circt::esi::ChannelType>();
185 void UnwrapValidReadyOp::build(OpBuilder &b, OperationState &state,
186 Value inChan, Value ready) {
187 auto inChanType = inChan.getType().cast<ChannelType>();
188 build(b, state, inChanType.getInner(), b.getI1Type(), inChan, ready);
191 circt::esi::ChannelType UnwrapValidReadyOp::channelType() {
192 return getChanInput().getType().cast<circt::esi::ChannelType>();
195 circt::esi::ChannelType WrapFIFOOp::channelType() {
196 return getChanOutput().getType().cast<circt::esi::ChannelType>();
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 getChanInput().getType().cast<circt::esi::ChannelType>();
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 operands[0].getType().cast<ChannelType>().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() {
283 getInterfaceSink().getType().cast<circt::sv::ModportType>();
284 auto chanType = getOutput().getType().cast<ChannelType>();
288 circt::esi::ChannelType WrapSVInterfaceOp::channelType() {
289 return getOutput().getType().cast<circt::esi::ChannelType>();
292 LogicalResult UnwrapSVInterfaceOp::verify() {
294 getInterfaceSource().getType().cast<circt::sv::ModportType>();
295 auto chanType = getChanInput().getType().cast<ChannelType>();
299 circt::esi::ChannelType UnwrapSVInterfaceOp::channelType() {
300 return getChanInput().getType().cast<circt::esi::ChannelType>();
303 LogicalResult WrapWindow::verify() {
304 hw::UnionType expectedInput = getWindow().getType().getLoweredType();
305 if (expectedInput == getFrame().getType())
307 return emitOpError(
"Expected input type is ") << expectedInput;
312 ValueRange operands, DictionaryAttr,
313 mlir::OpaqueProperties, mlir::RegionRange,
314 SmallVectorImpl<Type> &inferredReturnTypes) {
315 auto windowType = operands.front().getType().cast<WindowType>();
316 inferredReturnTypes.push_back(windowType.getLoweredType());
323 if (p.parseType(window))
326 frame = window.getLoweredType();
342 hw::InnerRefAttr servicePort) {
343 ModuleOp top = op->getParentOfType<mlir::ModuleOp>();
344 SymbolTable &topSyms = symbolTable.getSymbolTable(top);
346 StringAttr modName = servicePort.getModule();
347 auto serviceDecl = topSyms.lookup<ServiceDeclOpInterface>(modName);
349 return op->emitOpError(
"Could not find service declaration ")
350 << servicePort.getModuleRef();
357 hw::InnerRefAttr servicePort) {
359 if (failed(serviceDecl))
361 auto portInfo = serviceDecl->getPortInfo(servicePort.getName());
362 if (failed(portInfo))
363 return op->emitOpError(
"Could not locate port ") << servicePort.getName();
370 ChannelBundleType svcBundleType,
371 ChannelBundleType reqBundleType,
372 bool skipDirectionCheck) {
373 auto *ctxt = svcBundleType.getContext();
376 if (svcBundleType.getChannels().size() != reqBundleType.getChannels().size())
377 return req->emitOpError(
378 "Request port bundle channel count does not match service "
379 "port bundle channel count");
382 DenseMap<StringAttr, BundledChannel> declBundleChannels;
384 declBundleChannels[bc.name] = bc;
388 auto f = declBundleChannels.find(bc.name);
389 if (f == declBundleChannels.end())
390 return req->emitOpError(
391 "Request channel name not found in service port bundle");
392 if (!skipDirectionCheck && f->second.direction != bc.direction)
393 return req->emitOpError(
394 "Request channel direction does not match service "
395 "port bundle channel direction");
397 if (f->second.type != bc.type && f->second.type != anyChannelType)
398 return req->emitOpError(
399 "Request channel type does not match service port "
400 "bundle channel type");
405 LogicalResult RequestToClientConnectionOp::verifySymbolUses(
406 SymbolTableCollection &symbolTable) {
411 return emitOpError(
"Service port is not a to-client port");
412 return checkTypeMatch(*
this, svcPort->type, getToClient().getType(),
false);
415 LogicalResult RequestToServerConnectionOp::verifySymbolUses(
416 SymbolTableCollection &symbolTable) {
421 return emitOpError(
"Service port is not a to-server port");
422 return checkTypeMatch(*
this, svcPort->type, getToServer().getType(),
false);
425 LogicalResult ServiceImplementConnReqOp::verifySymbolUses(
426 SymbolTableCollection &symbolTable) {
430 return checkTypeMatch(*
this, svcPort->type, getToClient().getType(),
true);
434 for (
auto toServer : getOps<ToServerOp>())
438 for (
auto toClient : getOps<ToClientOp>())
450 SmallVectorImpl<Type> &toChannelTypes,
451 SmallVectorImpl<Type> &fromChannelTypes, Type &type) {
453 ChannelBundleType bundleType;
454 if (parser.parseType(bundleType))
459 if (ch.direction == ChannelDirection::to)
460 toChannelTypes.push_back(ch.type);
461 else if (ch.direction == ChannelDirection::from)
462 fromChannelTypes.push_back(ch.type);
464 assert(
false &&
"Channel direction invalid");
467 template <
typename T3,
typename T4>
470 p.printType(bundleType);
472 void UnpackBundleOp::build(::mlir::OpBuilder &odsBuilder,
473 ::mlir::OperationState &odsState, Value bundle,
474 mlir::ValueRange fromChannels) {
476 cast<ChannelBundleType>(bundle.getType()).getChannels())
477 if (ch.direction == ChannelDirection::to)
478 odsState.addTypes(ch.type);
479 odsState.addOperands(bundle);
480 odsState.addOperands(fromChannels);
483 LogicalResult PackBundleOp::verify() {
484 if (!getBundle().hasOneUse())
485 return emitOpError(
"bundles must have exactly one user");
488 void PackBundleOp::build(::mlir::OpBuilder &odsBuilder,
489 ::mlir::OperationState &odsState,
490 ChannelBundleType bundleType,
491 mlir::ValueRange toChannels) {
492 odsState.addTypes(bundleType);
493 for (
BundledChannel ch : cast<ChannelBundleType>(bundleType).getChannels())
494 if (ch.direction == ChannelDirection::from)
495 odsState.addTypes(ch.type);
496 odsState.addOperands(toChannels);
499 LogicalResult UnpackBundleOp::verify() {
500 if (!getBundle().hasOneUse())
501 return emitOpError(
"bundles must have exactly one user");
506 if (getNumResults() == 0)
508 setNameFn(getResult(0),
"bundle");
509 for (
auto [idx, from] : llvm::enumerate(llvm::make_filter_range(
511 return ch.
direction == ChannelDirection::from;
513 if (idx + 1 < getNumResults())
514 setNameFn(getResult(idx + 1), from.name.getValue());
518 for (
auto [idx, to] : llvm::enumerate(llvm::make_filter_range(
520 return ch.
direction == ChannelDirection::to;
522 if (idx < getNumResults())
523 setNameFn(getResult(idx), to.name.getValue());
529 LogicalResult ESIPureModuleOp::verify() {
530 ESIDialect *esiDialect = getContext()->getLoadedDialect<ESIDialect>();
531 Block &body = getBody().front();
532 auto channelOrOutput = [](Value v) {
533 if (v.getType().isa<ChannelType, ChannelBundleType>())
535 if (v.getUsers().empty())
537 return llvm::all_of(v.getUsers(), [](Operation *op) {
538 return isa<ESIPureModuleOutputOp>(op);
542 DenseMap<StringAttr, std::tuple<hw::ModulePort::Direction, Type, Operation *>>
544 for (Operation &op : body.getOperations()) {
545 if (igraph::InstanceOpInterface inst =
546 dyn_cast<igraph::InstanceOpInterface>(op)) {
547 if (llvm::any_of(op.getOperands(), [](Value v) {
548 return !(v.getType().isa<ChannelType, ChannelBundleType>() ||
549 isa<ESIPureModuleInputOp>(v.getDefiningOp()));
551 return inst.emitOpError(
552 "instances in ESI pure modules can only contain channel ports or "
553 "ports driven by 'input' ops");
554 if (!llvm::all_of(op.getResults(), channelOrOutput))
555 return inst.emitOpError(
556 "instances in ESI pure modules can only contain channel ports or "
557 "drive only 'outputs'");
560 if (op.getDialect() != esiDialect)
561 return op.emitOpError(
"operation not allowed in ESI pure modules");
565 if (
auto port = dyn_cast<ESIPureModuleInputOp>(op)) {
566 auto existing = ports.find(port.getNameAttr());
567 Type portType = port.getResult().getType();
568 if (existing != ports.end()) {
569 auto [dir, type, op] = existing->getSecond();
571 return (port.emitOpError(
"port '")
572 << port.getName() <<
"' previously declared as type " << type)
573 .attachNote(op->getLoc());
575 ports[port.getNameAttr()] = std::make_tuple(
577 }
else if (
auto port = dyn_cast<ESIPureModuleOutputOp>(op)) {
578 auto existing = ports.find(port.getNameAttr());
579 if (existing != ports.end())
580 return (port.emitOpError(
"port '")
581 << port.getName() <<
"' previously declared")
582 .attachNote(std::get<2>(existing->getSecond())->getLoc());
583 ports[port.getNameAttr()] =
585 port.getValue().getType(), port.getOperation());
591 hw::ModuleType ESIPureModuleOp::getHWModuleType() {
597 ::llvm::report_fatal_error(
"not supported");
601 size_t ESIPureModuleOp::getNumInputPorts() {
return 0; }
602 size_t ESIPureModuleOp::getNumOutputPorts() {
return 0; }
603 size_t ESIPureModuleOp::getPortIdForInputId(
size_t) {
604 assert(0 &&
"Out of bounds input port id");
607 size_t ESIPureModuleOp::getPortIdForOutputId(
size_t) {
608 assert(0 &&
"Out of bounds output port id");
613 SmallVector<Location> retval;
618 emitError(
"No ports for port locations");
622 emitError(
"No ports for port naming");
625 void ESIPureModuleOp::setAllPortAttrs(ArrayRef<Attribute> attrs) {
626 emitError(
"No ports for port attributes");
629 void ESIPureModuleOp::removeAllPortAttrs() {
630 emitError(
"No ports for port attributes)");
633 SmallVector<Attribute> ESIPureModuleOp::getAllPortAttrs() {
634 SmallVector<Attribute> retval;
639 emitError(
"No ports for port types");
646 StringRef ServiceImplRecordOp::getManifestClass() {
return "service"; }
648 void ServiceImplRecordOp::getDetails(SmallVectorImpl<NamedAttribute> &results) {
649 auto *ctxt = getContext();
651 results.emplace_back(getAppIDAttrName(), getAppIDAttr());
653 results.emplace_back(getServiceAttrName(), getServiceAttr());
654 results.emplace_back(getServiceImplNameAttrName(), getServiceImplNameAttr());
656 for (
auto implDetail : getImplDetailsAttr().getValue())
657 results.push_back(implDetail);
660 SmallVector<Attribute, 8> reqDetails;
661 for (
auto reqDetail : getReqDetails().front().getOps<IsManifestData>())
662 reqDetails.push_back(reqDetail.getDetailsAsDict());
667 StringRef ServiceImplClientRecordOp::getManifestClass() {
668 return "service_client";
670 void ServiceImplClientRecordOp::getDetails(
671 SmallVectorImpl<NamedAttribute> &results) {
675 results.emplace_back(getRelAppIDPathAttrName(), getRelAppIDPathAttr());
676 results.emplace_back(getServicePortAttrName(), getServicePortAttr());
678 for (
auto implDetail : getImplDetailsAttr().getValue())
679 results.push_back(implDetail);
682 StringRef ServiceRequestRecordOp::getManifestClass() {
return "client_port"; }
684 void ServiceRequestRecordOp::getDetails(
685 SmallVectorImpl<NamedAttribute> &results) {
686 auto *ctxt = getContext();
687 results.emplace_back(
StringAttr::get(ctxt,
"appID"), getRequestorAttr());
688 results.emplace_back(
689 getDirectionAttrName(),
691 results.emplace_back(getBundleTypeAttrName(), getBundleTypeAttr());
692 results.emplace_back(getServicePortAttrName(), getServicePortAttr());
695 StringRef SymbolMetadataOp::getManifestClass() {
return "sym_info"; }
697 #define GET_OP_CLASSES
698 #include "circt/Dialect/ESI/ESI.cpp.inc"
700 #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.
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)
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 void setAllPortLocs(ArrayRef< Location > locs, ModTy module)
static hw::ModulePort::Direction getDirection(Type type)
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.
This file defines an intermediate representation for circuits acting as an abstraction for constraint...
function_ref< void(Value, StringRef)> OpAsmSetValueNameFn
ChannelDirection direction
Describes a service port.
This holds the name, type, direction of a module's ports.