22 #include "mlir/IR/BuiltinTypes.h"
23 #include "mlir/IR/ImplicitLocOpBuilder.h"
28 using namespace circt;
33 ServiceDeclOpInterface decl) {
39 return req.emitOpError(
"Could not find service generator for attribute '")
40 << req.getImplTypeAttr() <<
"'";
47 auto implRecord = b.create<ServiceImplRecordOp>(
48 req.getLoc(), req.getAppID(), req.getServiceSymbolAttr(),
49 req.getStdServiceAttr(), req.getImplTypeAttr(), b.getDictionaryAttr({}));
50 implRecord.getReqDetails().emplaceBlock();
52 return genF->second(req, decl, implRecord);
58 ServiceDeclOpInterface,
59 ServiceImplRecordOp implRecord) {
60 auto *
ctxt = implReq.getContext();
62 Value clk = implReq.getOperand(0);
63 Value rst = implReq.getOperand(1);
65 if (implReq.getImplOpts()) {
66 auto opts = implReq.getImplOpts()->getValue();
67 for (
auto nameAttr : opts) {
68 return implReq.emitOpError(
"did not recognize option name ")
69 << nameAttr.getName();
73 Block &connImplBlock = implRecord.getReqDetails().front();
74 OpBuilder implRecords = OpBuilder::atBlockEnd(&connImplBlock);
77 auto toStringAttr = [&](ArrayAttr strArr, StringAttr channelName) {
79 llvm::raw_string_ostream os(buff);
81 strArr.getAsRange<AppIDAttr>(), os,
82 [&](AppIDAttr appid) {
83 os << appid.getName().getValue();
85 os <<
"[" << appid.getIndex() <<
"]";
88 os <<
"." << channelName.getValue();
92 llvm::DenseMap<ServiceImplementConnReqOp, unsigned> toClientResultNum;
93 for (
auto req : implReq.getOps<ServiceImplementConnReqOp>())
94 toClientResultNum[req] = toClientResultNum.size();
101 for (
auto req : implReq.getOps<ServiceImplementConnReqOp>()) {
102 Location loc = req->getLoc();
103 ChannelBundleType bundleType = req.getToClient().getType();
104 SmallVector<NamedAttribute, 8> channelAssignments;
106 SmallVector<Value, 8> toServerValues;
108 if (ch.direction == ChannelDirection::to) {
109 auto cosim = b.create<CosimFromHostEndpointOp>(
110 loc, ch.type, clk, rst,
111 toStringAttr(req.getRelativeAppIDPathAttr(), ch.name));
112 toServerValues.push_back(cosim.getFromHost());
113 channelAssignments.push_back(
114 b.getNamedAttr(ch.name, cosim.getIdAttr()));
119 b.create<PackBundleOp>(implReq.getLoc(), bundleType, toServerValues);
120 implReq.getResult(toClientResultNum[req])
121 .replaceAllUsesWith(pack.getBundle());
125 if (ch.direction == ChannelDirection::from) {
126 auto cosim = b.create<CosimToHostEndpointOp>(
127 loc, clk, rst, pack.getFromChannels()[chanIdx++],
128 toStringAttr(req.getRelativeAppIDPathAttr(), ch.name));
129 channelAssignments.push_back(
130 b.getNamedAttr(ch.name, cosim.getIdAttr()));
134 implRecords.create<ServiceImplClientRecordOp>(
135 req.getLoc(), req.getRelativeAppIDPathAttr(), req.getServicePortAttr(),
137 b.getDictionaryAttr(b.getNamedAttr(
138 "channel_assignments", b.getDictionaryAttr(channelAssignments))));
150 ServiceDeclOpInterface decl,
151 ServiceImplRecordOp) {
153 return implReq.emitOpError(
154 "Must specify a service declaration to use 'sv_mem'.");
156 ImplicitLocOpBuilder b(implReq.getLoc(), implReq);
159 RandomAccessMemoryDeclOp ramDecl =
160 dyn_cast<RandomAccessMemoryDeclOp>(decl.getOperation());
162 return implReq.emitOpError(
163 "'sv_mem' implementation type can only be used to "
164 "implement RandomAccessMemory declarations");
166 if (implReq.getNumOperands() != 2)
167 return implReq.emitOpError(
"Implementation requires clk and rst operands");
168 auto clk = implReq.getOperand(0);
169 auto rst = implReq.getOperand(1);
170 auto write = b.getStringAttr(
"write");
171 auto read = b.getStringAttr(
"read");
173 APInt( 0, 0,
false));
174 auto i1 = b.getI1Type();
178 SmallVector<ServiceImplementConnReqOp, 8> toClientReqs(
179 llvm::make_filter_range(
180 implReq.getOps<ServiceImplementConnReqOp>(),
181 [](
auto req) { return req.getToClient() != nullptr; }));
184 DenseMap<Value, Value> outputMap;
185 for (
auto [bout, reqout] :
186 llvm::zip_longest(toClientReqs, implReq.getResults())) {
188 assert(reqout.has_value());
189 Value toClient = bout->getToClient();
190 outputMap[toClient] = *reqout;
194 hw::UnpackedArrayType memType =
197 b.create<
sv::RegOp>(memType, implReq.getServiceSymbolAttr().getAttr())
202 SmallVector<std::tuple<Value, Value, Value>> writeGoAddressData;
203 for (
auto req : implReq.getOps<ServiceImplementConnReqOp>()) {
204 auto port = req.getServicePort().getName();
211 auto doneValid = bb.
get(i1);
212 auto ackChannel = b.create<WrapValidReadyOp>(none, doneValid);
215 b.create<PackBundleOp>(implReq.getLoc(), req.getToClient().getType(),
216 ackChannel.getChanOutput());
218 pack.getFromChannels()[RandomAccessMemoryDeclOp::ReqDirChannelIdx];
219 toClientResp = pack.getBundle();
223 b.create<UnwrapValidReadyOp>(toServer, ackChannel.getReady());
226 b.getStringAttr(
"address"));
228 b.getStringAttr(
"data"));
232 go->setAttr(
"sv.namehint", b.getStringAttr(
"write_go"));
237 writeGoAddressData.push_back(std::make_tuple(go, address, data));
239 }
else if (port == read) {
243 auto dataValid = bb.
get(i1);
244 auto data = bb.
get(ramDecl.getInnerType());
245 auto dataChannel = b.create<WrapValidReadyOp>(data, dataValid);
248 b.create<PackBundleOp>(implReq.getLoc(), req.getToClient().getType(),
249 dataChannel.getChanOutput());
251 pack.getFromChannels()[RandomAccessMemoryDeclOp::RespDirChannelIdx];
252 toClientResp = pack.getBundle();
256 b.create<UnwrapValidReadyOp>(toServer, dataChannel.getReady());
258 b.create<sv::ArrayIndexInOutOp>(mem, addressUnwrap.getRawOutput());
262 data.setValue(readData);
263 dataValid.setValue(addressUnwrap.getValid());
265 assert(
false &&
"Port should be either 'read' or 'write'");
268 outputMap[req.getToClient()].replaceAllUsesWith(toClientResp);
272 auto hwClk = b.create<seq::FromClockOp>(clk);
273 b.create<sv::AlwaysFFOp>(
274 sv::EventControl::AtPosEdge, hwClk, ResetType::SyncReset,
275 sv::EventControl::AtPosEdge, rst, [&] {
276 for (
auto [go, address, data] : writeGoAddressData) {
277 Value a = address, d = data;
279 b.create<sv::IfOp>(go, [&] {
280 Value memLoc = b.create<sv::ArrayIndexInOutOp>(mem, a);
281 b.create<sv::PAssignOp>(memLoc, d);
291 DenseMap<StringRef, ServiceGeneratorDispatcher::ServiceGeneratorFunc>{
313 struct ESIConnectServicesPass
314 :
public ESIConnectServicesBase<ESIConnectServicesPass>,
318 : genDispatcher(gen) {}
319 ESIConnectServicesPass()
322 void runOnOperation()
override;
327 void convertReq(RequestConnectionOp);
332 LogicalResult surfaceReqs(hw::HWMutableModuleLike,
333 ArrayRef<ServiceImplementConnReqOp>);
338 LogicalResult replaceInst(ServiceInstanceOp, Block *portReqs);
342 LogicalResult process(hw::HWModuleLike);
345 StringAttr getStdService(FlatSymbolRefAttr serviceSym);
352 void ESIConnectServicesPass::runOnOperation() {
353 ModuleOp outerMod = getOperation();
354 topLevelSyms.addDefinitions(outerMod);
356 outerMod.walk([&](RequestConnectionOp req) { convertReq(req); });
362 SmallVector<hw::HWModuleLike, 64> sortedMods;
363 getAndSortModules(outerMod, sortedMods);
366 for (
auto mod : sortedMods) {
367 hw::HWModuleLike mutableMod = dyn_cast<hw::HWModuleLike>(*mod);
368 if (mutableMod && failed(process(mutableMod))) {
376 StringAttr ESIConnectServicesPass::getStdService(FlatSymbolRefAttr svcSym) {
379 Operation *svcDecl = topLevelSyms.getDefinition(svcSym);
380 if (!isa<CustomServiceDeclOp>(svcDecl))
381 return svcDecl->getName().getIdentifier();
385 void ESIConnectServicesPass::convertReq(RequestConnectionOp req) {
387 auto newReq = b.create<ServiceImplementConnReqOp>(
388 req.getLoc(), req.getToClient().getType(), req.getServicePortAttr(),
390 newReq->setDialectAttrs(req->getDialectAttrs());
391 req.getToClient().replaceAllUsesWith(newReq.getToClient());
394 b.create<ServiceRequestRecordOp>(
395 req.getLoc(), req.getAppID(), req.getServicePortAttr(),
396 getStdService(req.getServicePortAttr().getModuleRef()),
397 req.getToClient().getType());
401 LogicalResult ESIConnectServicesPass::process(hw::HWModuleLike mod) {
403 if (mod->getNumRegions() == 0 || mod->getRegion(0).empty())
406 Block &modBlock = mod->getRegion(0).front();
409 SmallVector<ServiceImplementConnReqOp, 4> nonLocalReqs;
411 DenseMap<SymbolRefAttr, Block *> localImplReqs;
412 Block *anyServiceInst =
nullptr;
413 for (
auto instOp : modBlock.getOps<ServiceInstanceOp>()) {
414 auto *b =
new Block();
415 localImplReqs[instOp.getServiceSymbolAttr()] = b;
416 if (!instOp.getServiceSymbolAttr())
421 mod.walk([&](ServiceImplementConnReqOp req) {
422 auto service = req.getServicePort().getModuleRef();
423 auto implOpF = localImplReqs.find(service);
424 if (implOpF != localImplReqs.end())
425 req->moveBefore(implOpF->second, implOpF->second->end());
426 else if (anyServiceInst)
427 req->moveBefore(anyServiceInst, anyServiceInst->end());
429 nonLocalReqs.push_back(req);
435 llvm::make_early_inc_range(modBlock.getOps<ServiceInstanceOp>())) {
436 Block *portReqs = localImplReqs[instOp.getServiceSymbolAttr()];
437 if (failed(replaceInst(instOp, portReqs)))
442 if (nonLocalReqs.empty())
445 if (
auto mutableMod = dyn_cast<hw::HWMutableModuleLike>(mod.getOperation()))
446 return surfaceReqs(mutableMod, nonLocalReqs);
447 return mod.emitOpError(
448 "Cannot surface requests through module without mutable ports");
451 LogicalResult ESIConnectServicesPass::replaceInst(ServiceInstanceOp instOp,
454 auto declSym = instOp.getServiceSymbolAttr();
455 ServiceDeclOpInterface decl;
457 decl = dyn_cast_or_null<ServiceDeclOpInterface>(
458 topLevelSyms.getDefinition(declSym));
460 return instOp.emitOpError(
"Could not find service declaration ")
466 SmallVector<Type, 8> resultTypes(instOp.getResultTypes().begin(),
467 instOp.getResultTypes().end());
468 for (
auto req : portReqs->getOps<ServiceImplementConnReqOp>())
469 resultTypes.push_back(req.getBundleType());
473 auto implOp = b.create<ServiceImplementReqOp>(
474 instOp.getLoc(), resultTypes, instOp.getAppIDAttr(),
475 instOp.getServiceSymbolAttr(), instOp.getImplTypeAttr(),
476 getStdService(declSym), instOp.getImplOptsAttr(), instOp.getOperands());
477 implOp->setDialectAttrs(instOp->getDialectAttrs());
478 implOp.getPortReqs().push_back(portReqs);
481 for (
auto [n, o] : llvm::zip(implOp.getResults(), instOp.getResults()))
482 o.replaceAllUsesWith(n);
483 unsigned instOpNumResults = instOp.getNumResults();
484 for (
auto [idx, req] :
485 llvm::enumerate(portReqs->getOps<ServiceImplementConnReqOp>())) {
486 req.getToClient().replaceAllUsesWith(
487 implOp.getResult(idx + instOpNumResults));
491 if (failed(genDispatcher.generate(implOp, decl)))
492 return instOp.emitOpError(
"failed to generate server");
499 ESIConnectServicesPass::surfaceReqs(hw::HWMutableModuleLike mod,
500 ArrayRef<ServiceImplementConnReqOp> reqs) {
501 auto *
ctxt = mod.getContext();
502 Block *body = &mod->getRegion(0).front();
505 unsigned origNumInputs = mod.getNumInputPorts();
506 SmallVector<std::pair<unsigned, hw::PortInfo>> newInputs;
509 auto getPortName = [&](ArrayAttr namePath) {
510 std::string portName;
511 llvm::raw_string_ostream nameOS(portName);
513 namePath.getAsRange<AppIDAttr>(), nameOS,
514 [&](AppIDAttr appid) {
515 nameOS << appid.getName().getValue();
516 if (appid.getIndex())
517 nameOS <<
"_" << appid.getIndex();
523 for (
auto req : reqs)
524 if (req->getParentWithTrait<OpTrait::IsIsolatedFromAbove>() != mod)
525 return req.emitOpError(
526 "Cannot surface requests through isolated from above ops");
529 for (
auto req : reqs) {
530 newInputs.push_back(std::make_pair(
532 hw::PortInfo{{getPortName(req.getRelativeAppIDPathAttr()),
539 Value replValue = body->addArgument(req.getBundleType(), req->getLoc());
540 req.getToClient().replaceAllUsesWith(replValue);
542 mod.insertPorts(newInputs, {});
545 auto prependNamePart = [&](ArrayAttr appIDPath, AppIDAttr appID) {
546 SmallVector<Attribute, 8> newAppIDPath;
547 newAppIDPath.push_back(appID);
548 newAppIDPath.append(appIDPath.begin(), appIDPath.end());
553 SmallVector<igraph::InstanceOpInterface, 1> newModuleInstantiations;
554 for (
auto inst : moduleInstantiations[mod]) {
558 SmallVector<Value, 16> newOperands;
559 for (
auto req : reqs) {
561 ArrayAttr appIDPath = req.getRelativeAppIDPathAttr();
562 if (
auto instAppID = dyn_cast_or_null<AppIDAttr>(
563 inst->getDiscardableAttr(AppIDAttr::AppIDAttrName)))
564 appIDPath = prependNamePart(appIDPath, instAppID);
567 auto clone = b.create<ServiceImplementConnReqOp>(
568 req.getLoc(), req.getToClient().getType(), req.getServicePortAttr(),
570 clone->setDialectAttrs(req->getDialectAttrs());
571 newOperands.push_back(clone.getToClient());
573 inst->insertOperands(inst->getNumOperands(), newOperands);
575 if (
auto hwInst = dyn_cast<hw::InstanceOp>(*inst))
576 hwInst.setArgNamesAttr(b.getArrayAttr(mod.getInputNames()));
581 for (
auto req : reqs)
586 std::unique_ptr<OperationPass<ModuleOp>>
588 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)
Instantiate one of these and use it to build typed backedges.
Backedge get(mlir::Type resultType, mlir::LocationAttr optionalLoc={})
Create a typed backedge.
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
def create(data_type, value)
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
std::unique_ptr< OperationPass< ModuleOp > > createESIConnectServicesPass()
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
A set of methods which are broadly useful in a number of dialects.