22#include "mlir/IR/BuiltinTypes.h"
23#include "mlir/IR/ImplicitLocOpBuilder.h"
30#define GEN_PASS_DEF_ESICONNECTSERVICES
31#include "circt/Dialect/ESI/ESIPasses.h.inc"
45 ServiceDeclOpInterface,
46 ServiceImplRecordOp implRecord) {
47 auto *ctxt = implReq.getContext();
49 Value clk = implReq.getOperand(0);
50 Value rst = implReq.getOperand(1);
52 if (implReq.getImplOpts()) {
53 auto opts = implReq.getImplOpts()->getValue();
54 for (
auto nameAttr : opts) {
55 return implReq.emitOpError(
"did not recognize option name ")
56 << nameAttr.getName();
60 Block &connImplBlock = implRecord.getReqDetails().front();
61 implRecord.setIsEngine(
true);
62 OpBuilder implRecords = OpBuilder::atBlockEnd(&connImplBlock);
65 auto toStringAttr = [&](ArrayAttr strArr, StringAttr channelName) {
67 llvm::raw_string_ostream os(buff);
69 strArr.getAsRange<AppIDAttr>(), os,
70 [&](AppIDAttr appid) {
71 os << appid.getName().getValue();
73 os <<
"[" << appid.getIndex() <<
"]";
76 os <<
"." << channelName.getValue();
77 return StringAttr::get(ctxt, os.str());
80 auto getAssignment = [&](StringAttr name, StringAttr channelName) {
81 DictionaryAttr assignment = b.getDictionaryAttr({
82 b.getNamedAttr(
"type", b.getStringAttr(
"cosim")),
83 b.getNamedAttr(
"name", channelName),
85 return b.getNamedAttr(name, assignment);
88 llvm::DenseMap<ServiceImplementConnReqOp, unsigned> toClientResultNum;
89 for (
auto req : implReq.getOps<ServiceImplementConnReqOp>())
90 toClientResultNum[req] = toClientResultNum.size();
97 for (
auto req : implReq.getOps<ServiceImplementConnReqOp>()) {
98 Location loc = req->getLoc();
99 ChannelBundleType bundleType = req.getToClient().getType();
100 SmallVector<NamedAttribute, 8> channelAssignments;
102 SmallVector<Value, 8> toServerValues;
104 if (ch.direction == ChannelDirection::to) {
105 ChannelType fromHostType = ch.type;
106 if (fromHostType.getSignaling() == ChannelSignaling::FIFO)
107 fromHostType = b.getType<ChannelType>(fromHostType.getInner(),
108 ChannelSignaling::ValidReady,
109 fromHostType.getDataDelay());
110 auto cosim = b.create<CosimFromHostEndpointOp>(
111 loc, fromHostType, clk, rst,
112 toStringAttr(req.getRelativeAppIDPathAttr(), ch.name));
113 mlir::TypedValue<ChannelType> fromHost = cosim.getFromHost();
114 if (fromHostType.getSignaling() == ChannelSignaling::FIFO)
115 fromHost = b.create<ChannelBufferOp>(
116 loc, ch.type, clk, rst, fromHost,
117 b.getIntegerAttr(b.getI64Type(), 1),
120 toServerValues.push_back(fromHost);
121 channelAssignments.push_back(getAssignment(ch.name, cosim.getIdAttr()));
126 b.create<PackBundleOp>(implReq.getLoc(), bundleType, toServerValues);
127 implReq.getResult(toClientResultNum[req])
128 .replaceAllUsesWith(pack.getBundle());
132 if (ch.direction == ChannelDirection::from) {
133 Value fromChannel = pack.getFromChannels()[chanIdx++];
134 auto chType = cast<ChannelType>(fromChannel.getType());
135 if (chType.getSignaling() == ChannelSignaling::FIFO) {
136 auto cosimType = b.getType<ChannelType>(chType.getInner(),
137 ChannelSignaling::ValidReady,
138 chType.getDataDelay());
139 fromChannel = b.create<ChannelBufferOp>(
140 loc, cosimType, clk, rst, fromChannel,
141 b.getIntegerAttr(b.getI64Type(), 1),
145 auto cosim = b.create<CosimToHostEndpointOp>(
146 loc, clk, rst, fromChannel,
147 toStringAttr(req.getRelativeAppIDPathAttr(), ch.name));
148 channelAssignments.push_back(getAssignment(ch.name, cosim.getIdAttr()));
152 implRecords.create<ServiceImplClientRecordOp>(
153 req.getLoc(), req.getRelativeAppIDPathAttr(), req.getServicePortAttr(),
154 TypeAttr::get(bundleType), b.getDictionaryAttr(channelAssignments),
167 ServiceDeclOpInterface decl,
168 ServiceImplRecordOp) {
170 return implReq.emitOpError(
171 "Must specify a service declaration to use 'sv_mem'.");
173 ImplicitLocOpBuilder b(implReq.getLoc(), implReq);
176 RandomAccessMemoryDeclOp ramDecl =
177 dyn_cast<RandomAccessMemoryDeclOp>(decl.getOperation());
179 return implReq.emitOpError(
180 "'sv_mem' implementation type can only be used to "
181 "implement RandomAccessMemory declarations");
183 if (implReq.getNumOperands() != 2)
184 return implReq.emitOpError(
"Implementation requires clk and rst operands");
185 auto clk = implReq.getOperand(0);
186 auto rst = implReq.getOperand(1);
187 auto write = b.getStringAttr(
"write");
188 auto read = b.getStringAttr(
"read");
190 APInt( 0, 0,
false));
191 auto i1 = b.getI1Type();
195 SmallVector<ServiceImplementConnReqOp, 8> toClientReqs(
196 llvm::make_filter_range(
197 implReq.getOps<ServiceImplementConnReqOp>(),
198 [](
auto req) { return req.getToClient() != nullptr; }));
201 DenseMap<Value, Value> outputMap;
202 for (
auto [bout, reqout] :
203 llvm::zip_longest(toClientReqs, implReq.getResults())) {
205 assert(reqout.has_value());
206 Value toClient = bout->getToClient();
207 outputMap[toClient] = *reqout;
211 hw::UnpackedArrayType memType =
212 hw::UnpackedArrayType::get(ramDecl.getInnerType(), ramDecl.getDepth());
214 b.create<
sv::RegOp>(memType, implReq.getServiceSymbolAttr().getAttr())
219 SmallVector<std::tuple<Value, Value, Value>> writeGoAddressData;
220 for (
auto req : implReq.getOps<ServiceImplementConnReqOp>()) {
221 auto port = req.getServicePort().getName();
228 auto doneValid = bb.
get(i1);
229 auto ackChannel = b.create<WrapValidReadyOp>(none, doneValid);
232 b.create<PackBundleOp>(implReq.getLoc(), req.getToClient().getType(),
233 ackChannel.getChanOutput());
235 pack.getFromChannels()[RandomAccessMemoryDeclOp::ReqDirChannelIdx];
236 toClientResp = pack.getBundle();
240 b.create<UnwrapValidReadyOp>(toServer, ackChannel.getReady());
243 b.getStringAttr(
"address"));
245 b.getStringAttr(
"data"));
249 go->setAttr(
"sv.namehint", b.getStringAttr(
"write_go"));
254 writeGoAddressData.push_back(std::make_tuple(go, address, data));
256 }
else if (port == read) {
260 auto dataValid = bb.
get(i1);
261 auto data = bb.
get(ramDecl.getInnerType());
262 auto dataChannel = b.create<WrapValidReadyOp>(data, dataValid);
265 b.create<PackBundleOp>(implReq.getLoc(), req.getToClient().getType(),
266 dataChannel.getChanOutput());
268 pack.getFromChannels()[RandomAccessMemoryDeclOp::RespDirChannelIdx];
269 toClientResp = pack.getBundle();
273 b.create<UnwrapValidReadyOp>(toServer, dataChannel.getReady());
275 b.create<sv::ArrayIndexInOutOp>(mem, addressUnwrap.getRawOutput());
279 data.setValue(readData);
280 dataValid.setValue(addressUnwrap.getValid());
282 assert(
false &&
"Port should be either 'read' or 'write'");
285 outputMap[req.getToClient()].replaceAllUsesWith(toClientResp);
289 auto hwClk = b.create<seq::FromClockOp>(clk);
290 b.create<sv::AlwaysFFOp>(
291 sv::EventControl::AtPosEdge, hwClk, sv::ResetType::SyncReset,
292 sv::EventControl::AtPosEdge, rst, [&] {
293 for (
auto [go, address, data] : writeGoAddressData) {
294 Value a = address, d = data;
296 b.create<sv::IfOp>(go, [&] {
297 Value memLoc = b.create<sv::ArrayIndexInOutOp>(mem, a);
298 b.create<sv::PAssignOp>(memLoc, d);
313 ServiceDeclOpInterface decl) {
316 auto genF =
genLookupTable.find(req.getImplTypeAttr().getValue());
319 return req.emitOpError(
"Could not find service generator for attribute '")
320 << req.getImplTypeAttr() <<
"'";
327 auto implRecord = b.create<ServiceImplRecordOp>(
328 req.getLoc(), req.getAppID(),
false,
329 req.getServiceSymbolAttr(), req.getStdServiceAttr(),
330 req.getImplTypeAttr(), b.getDictionaryAttr({}));
331 implRecord.getReqDetails().emplaceBlock();
333 return genF->second(req, decl, implRecord);
337 DenseMap<StringRef, ServiceGeneratorDispatcher::ServiceGeneratorFunc>{
343 return ::globalDispatcher;
365 DenseMap<Operation *, SmallVector<igraph::InstanceOpInterface, 1>>
366 moduleInstantiations;
368 void getAndSortModules(ModuleOp topMod,
369 SmallVectorImpl<hw::HWModuleLike> &mods);
370 void getAndSortModulesVisitor(hw::HWModuleLike mod,
371 SmallVectorImpl<hw::HWModuleLike> &mods,
372 DenseSet<Operation *> &modsSeen);
376void ModuleSorter::getAndSortModules(ModuleOp topMod,
377 SmallVectorImpl<hw::HWModuleLike> &mods) {
379 DenseSet<Operation *> modsSeen;
381 moduleInstantiations.clear();
382 topMod.walk([&](hw::HWModuleLike mod) {
383 getAndSortModulesVisitor(mod, mods, modsSeen);
388void ModuleSorter::getAndSortModulesVisitor(
389 hw::HWModuleLike mod, SmallVectorImpl<hw::HWModuleLike> &mods,
390 DenseSet<Operation *> &modsSeen) {
391 if (modsSeen.contains(mod))
393 modsSeen.insert(mod);
395 mod.walk([&](igraph::InstanceOpInterface inst) {
396 auto targetNameAttrs = inst.getReferencedModuleNamesAttr();
397 for (
auto targetNameAttr : targetNameAttrs) {
399 topLevelSyms.getDefinition(cast<StringAttr>(targetNameAttr));
401 moduleInstantiations[modOp].push_back(inst);
402 if (
auto modLike = dyn_cast<hw::HWModuleLike>(modOp))
403 getAndSortModulesVisitor(modLike, mods, modsSeen);
413struct ESIConnectServicesPass
414 :
public circt::esi::impl::ESIConnectServicesBase<ESIConnectServicesPass>,
418 : genDispatcher(gen) {}
419 ESIConnectServicesPass()
422 void runOnOperation()
override;
427 void convertReq(RequestConnectionOp);
432 LogicalResult surfaceReqs(hw::HWMutableModuleLike,
433 ArrayRef<ServiceImplementConnReqOp>);
438 LogicalResult replaceInst(ServiceInstanceOp,
439 ArrayRef<ServiceImplementConnReqOp> portReqs);
443 LogicalResult process(hw::HWModuleLike);
446 StringAttr getStdService(FlatSymbolRefAttr serviceSym);
453void ESIConnectServicesPass::runOnOperation() {
454 ModuleOp outerMod = getOperation();
455 topLevelSyms.addDefinitions(outerMod);
457 outerMod.walk([&](RequestConnectionOp req) { convertReq(req); });
463 SmallVector<hw::HWModuleLike, 64> sortedMods;
464 getAndSortModules(outerMod, sortedMods);
467 for (
auto mod : sortedMods) {
468 hw::HWModuleLike mutableMod = dyn_cast<hw::HWModuleLike>(*mod);
469 if (mutableMod && failed(process(mutableMod))) {
477StringAttr ESIConnectServicesPass::getStdService(FlatSymbolRefAttr svcSym) {
480 Operation *svcDecl = topLevelSyms.getDefinition(svcSym);
481 if (!isa<CustomServiceDeclOp>(svcDecl))
482 return svcDecl->getName().getIdentifier();
486void ESIConnectServicesPass::convertReq(RequestConnectionOp req) {
488 auto newReq = b.create<ServiceImplementConnReqOp>(
489 req.getLoc(), req.getToClient().getType(), req.getServicePortAttr(),
490 ArrayAttr::get(&getContext(), {req.getAppIDAttr()}));
491 newReq->setDialectAttrs(req->getDialectAttrs());
492 req.getToClient().replaceAllUsesWith(newReq.getToClient());
495 b.create<ServiceRequestRecordOp>(
496 req.getLoc(), req.getAppID(), req.getServicePortAttr(),
497 getStdService(req.getServicePortAttr().getModuleRef()),
498 req.getToClient().getType());
502LogicalResult ESIConnectServicesPass::process(hw::HWModuleLike mod) {
504 if (mod->getNumRegions() == 0 || mod->getRegion(0).empty())
507 Block &modBlock = mod->getRegion(0).front();
510 SetVector<ServiceImplementConnReqOp> nonLocalReqs;
512 llvm::MapVector<SymbolRefAttr, llvm::SetVector<ServiceImplementConnReqOp>>
514 for (
auto instOp : modBlock.getOps<ServiceInstanceOp>())
515 localImplReqs[instOp.getServiceSymbolAttr()] = {};
519 llvm::SetVector<ServiceImplementConnReqOp> *anyServiceInst =
nullptr;
520 if (
auto defaultService = localImplReqs.find(SymbolRefAttr());
521 defaultService != localImplReqs.end())
522 anyServiceInst = &defaultService->second;
524 auto sortConnReqs = [&]() {
526 for (
auto req :
llvm::make_early_inc_range(
527 mod.
getBodyBlock()->getOps<ServiceImplementConnReqOp>())) {
528 auto service = req.getServicePort().getModuleRef();
529 auto reqListIter = localImplReqs.find(service);
530 if (reqListIter != localImplReqs.end())
531 reqListIter->second.insert(req);
532 else if (anyServiceInst)
533 anyServiceInst->insert(req);
535 nonLocalReqs.insert(req);
544 llvm::make_early_inc_range(modBlock.getOps<ServiceInstanceOp>())) {
545 auto portReqs = localImplReqs[instOp.getServiceSymbolAttr()];
546 if (failed(replaceInst(instOp, portReqs.getArrayRef())))
550 for (RequestConnectionOp req :
llvm::make_early_inc_range(
557 if (nonLocalReqs.empty())
560 if (
auto mutableMod = dyn_cast<hw::HWMutableModuleLike>(mod.getOperation()))
561 return surfaceReqs(mutableMod, nonLocalReqs.getArrayRef());
562 return mod.emitOpError(
563 "Cannot surface requests through module without mutable ports");
566LogicalResult ESIConnectServicesPass::replaceInst(
567 ServiceInstanceOp instOp, ArrayRef<ServiceImplementConnReqOp> portReqs) {
568 auto declSym = instOp.getServiceSymbolAttr();
569 ServiceDeclOpInterface decl;
571 decl = dyn_cast_or_null<ServiceDeclOpInterface>(
572 topLevelSyms.getDefinition(declSym));
574 return instOp.emitOpError(
"Could not find service declaration ")
580 SmallVector<Type, 8> resultTypes(instOp.getResultTypes().begin(),
581 instOp.getResultTypes().end());
582 for (
auto req : portReqs)
583 resultTypes.push_back(req.getBundleType());
587 auto implOp = b.create<ServiceImplementReqOp>(
588 instOp.getLoc(), resultTypes, instOp.getAppIDAttr(),
589 instOp.getServiceSymbolAttr(), instOp.getImplTypeAttr(),
590 getStdService(declSym), instOp.getImplOptsAttr(), instOp.getOperands());
591 implOp->setDialectAttrs(instOp->getDialectAttrs());
592 Block &reqBlock = implOp.getPortReqs().emplaceBlock();
595 for (
auto [n, o] :
llvm::zip(implOp.getResults(), instOp.getResults()))
596 o.replaceAllUsesWith(n);
597 unsigned instOpNumResults = instOp.getNumResults();
598 for (
size_t idx = 0, e = portReqs.size(); idx < e; ++idx) {
599 ServiceImplementConnReqOp req = portReqs[idx];
600 req.getToClient().replaceAllUsesWith(
601 implOp.getResult(idx + instOpNumResults));
604 for (
auto req : portReqs)
605 req->moveBefore(&reqBlock, reqBlock.
end());
613 if (failed(genDispatcher.generate(implOp, decl)))
614 return implOp.emitOpError(
"failed to generate server");
620ESIConnectServicesPass::surfaceReqs(hw::HWMutableModuleLike mod,
621 ArrayRef<ServiceImplementConnReqOp> reqs) {
622 auto *
ctxt = mod.getContext();
623 Block *body = &mod->getRegion(0).front();
626 unsigned origNumInputs = mod.getNumInputPorts();
627 SmallVector<std::pair<unsigned, hw::PortInfo>> newInputs;
630 auto getPortName = [&](ArrayAttr namePath) {
631 std::string portName;
632 llvm::raw_string_ostream nameOS(portName);
634 namePath.getAsRange<AppIDAttr>(), nameOS,
635 [&](AppIDAttr appid) {
636 nameOS << appid.getName().getValue();
637 if (appid.getIndex())
638 nameOS <<
"_" << appid.getIndex();
641 return StringAttr::get(ctxt, nameOS.str());
644 for (
auto req : reqs)
645 if (req->getParentWithTrait<
OpTrait::IsIsolatedFromAbove>() != mod)
646 return req.emitOpError(
647 "Cannot surface requests through isolated from above ops");
650 for (
auto req : reqs) {
651 newInputs.push_back(std::make_pair(
653 hw::PortInfo{{getPortName(req.getRelativeAppIDPathAttr()),
660 Value replValue = body->addArgument(req.getBundleType(), req->getLoc());
661 req.getToClient().replaceAllUsesWith(replValue);
663 mod.insertPorts(newInputs, {});
666 auto prependNamePart = [&](ArrayAttr appIDPath, AppIDAttr appID) {
667 SmallVector<Attribute, 8> newAppIDPath;
668 newAppIDPath.push_back(appID);
669 newAppIDPath.append(appIDPath.begin(), appIDPath.end());
670 return ArrayAttr::get(appIDPath.getContext(), newAppIDPath);
674 SmallVector<igraph::InstanceOpInterface, 1> newModuleInstantiations;
675 for (
auto inst : moduleInstantiations[mod]) {
679 SmallVector<Value, 16> newOperands;
680 for (
auto req : reqs) {
682 ArrayAttr appIDPath = req.getRelativeAppIDPathAttr();
683 if (
auto instAppID = dyn_cast_or_null<AppIDAttr>(
684 inst->getDiscardableAttr(AppIDAttr::AppIDAttrName)))
685 appIDPath = prependNamePart(appIDPath, instAppID);
688 auto clone = b.create<ServiceImplementConnReqOp>(
689 req.getLoc(), req.getToClient().getType(), req.getServicePortAttr(),
691 clone->setDialectAttrs(req->getDialectAttrs());
692 newOperands.push_back(clone.getToClient());
694 inst->insertOperands(inst->getNumOperands(), newOperands);
696 if (
auto hwInst = dyn_cast<hw::InstanceOp>(*inst))
697 hwInst.setArgNamesAttr(b.getArrayAttr(mod.getInputNames()));
702 for (
auto req : reqs)
707std::unique_ptr<OperationPass<ModuleOp>>
709 return std::make_unique<ESIConnectServicesPass>();
assert(baseType &&"element must be base type")
static ServiceGeneratorDispatcher globalDispatcher(DenseMap< StringRef, ServiceGeneratorDispatcher::ServiceGeneratorFunc >{ {"cosim", instantiateCosimEndpointOps}, {"sv_mem", instantiateSystemVerilogMemory}}, false)
static LogicalResult instantiateCosimEndpointOps(ServiceImplementReqOp implReq, ServiceDeclOpInterface, ServiceImplRecordOp implRecord)
The generator for the "cosim" impl_type.
static LogicalResult instantiateSystemVerilogMemory(ServiceImplementReqOp implReq, ServiceDeclOpInterface decl, ServiceImplRecordOp)
static EvaluatorValuePtr unwrap(OMEvaluatorValue c)
static Block * getBodyBlock(FModuleLike mod)
Instantiate one of these and use it to build typed backedges.
Backedge get(mlir::Type resultType, mlir::LocationAttr optionalLoc={})
Create a typed backedge.
Default symbol cache implementation; stores associations between names (StringAttr's) to mlir::Operat...
Class which "dispatches" a service implementation request to its specified generator.
void registerGenerator(StringRef implType, ServiceGeneratorFunc gen)
Add a generator to this registry.
LogicalResult generate(ServiceImplementReqOp, ServiceDeclOpInterface)
Generate a service implementation if a generator exists in this registry.
static ServiceGeneratorDispatcher & globalDispatcher()
Get the global dispatcher.
DenseMap< StringRef, ServiceGeneratorFunc > genLookupTable
std::function< LogicalResult(ServiceImplementReqOp, ServiceDeclOpInterface, ServiceImplRecordOp)> ServiceGeneratorFunc
std::unique_ptr< OperationPass< ModuleOp > > createESIConnectServicesPass()
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
This holds the name, type, direction of a module's ports.