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() <<
"'";
43 return genF->second(req, decl);
48 ServiceDeclOpInterface) {
49 auto *ctxt = implReq.getContext();
51 Value clk = implReq.getOperand(0);
52 Value rst = implReq.getOperand(1);
53 SmallVector<NamedAttribute, 8> implDetails;
56 if (implReq.getImplOpts()) {
57 auto opts = implReq.getImplOpts()->getValue();
58 for (
auto nameAttr : opts) {
59 return implReq.emitOpError(
"did not recognize option name ")
60 << nameAttr.getName();
64 auto implRecord = b.create<ServiceImplRecordOp>(
65 implReq.getLoc(), implReq.getAppID(), implReq.getServiceSymbolAttr(),
66 b.getStringAttr(
"cosim"), b.getDictionaryAttr(implDetails));
67 Block &connImplBlock = implRecord.getReqDetails().emplaceBlock();
68 OpBuilder implRecords = OpBuilder::atBlockEnd(&connImplBlock);
71 auto toStringAttr = [&](ArrayAttr strArr, StringAttr channelName) {
73 llvm::raw_string_ostream os(buff);
75 strArr.getAsRange<AppIDAttr>(), os,
76 [&](AppIDAttr appid) {
77 os << appid.getName().getValue();
79 os <<
"[" << appid.getIndex() <<
"]";
82 os <<
"." << channelName.getValue();
86 llvm::DenseMap<ServiceImplementConnReqOp, unsigned> toClientResultNum;
87 for (
auto req : implReq.getOps<ServiceImplementConnReqOp>())
88 toClientResultNum[req] = toClientResultNum.size();
95 for (
auto req : implReq.getOps<ServiceImplementConnReqOp>()) {
96 Location loc = req->getLoc();
97 ChannelBundleType bundleType = req.getToClient().getType();
98 SmallVector<NamedAttribute, 8> channelAssignments;
100 SmallVector<Value, 8> toServerValues;
102 if (ch.direction == ChannelDirection::to) {
103 auto cosim = b.create<CosimFromHostEndpointOp>(
104 loc, ch.type, clk, rst,
105 toStringAttr(req.getRelativeAppIDPathAttr(), ch.name));
106 toServerValues.push_back(cosim.getFromHost());
107 channelAssignments.push_back(
108 b.getNamedAttr(ch.name, cosim.getNameAttr()));
113 b.create<PackBundleOp>(implReq.getLoc(), bundleType, toServerValues);
114 implReq.getResult(toClientResultNum[req])
115 .replaceAllUsesWith(pack.getBundle());
119 if (ch.direction == ChannelDirection::from) {
120 auto cosim = b.create<CosimToHostEndpointOp>(
121 loc, clk, rst, pack.getFromChannels()[chanIdx++],
122 toStringAttr(req.getRelativeAppIDPathAttr(), ch.name));
123 channelAssignments.push_back(
124 b.getNamedAttr(ch.name, cosim.getNameAttr()));
128 implRecords.create<ServiceImplClientRecordOp>(
129 req.getLoc(), req.getRelativeAppIDPathAttr(), req.getServicePortAttr(),
131 b.getDictionaryAttr(b.getNamedAttr(
132 "channel_assignments", b.getDictionaryAttr(channelAssignments))));
144 ServiceDeclOpInterface decl) {
146 return implReq.emitOpError(
147 "Must specify a service declaration to use 'sv_mem'.");
149 ImplicitLocOpBuilder b(implReq.getLoc(), implReq);
152 RandomAccessMemoryDeclOp ramDecl =
153 dyn_cast<RandomAccessMemoryDeclOp>(decl.getOperation());
155 return implReq.emitOpError(
156 "'sv_mem' implementation type can only be used to "
157 "implement RandomAccessMemory declarations");
159 if (implReq.getNumOperands() != 2)
160 return implReq.emitOpError(
"Implementation requires clk and rst operands");
161 auto clk = implReq.getOperand(0);
162 auto rst = implReq.getOperand(1);
163 auto write = b.getStringAttr(
"write");
164 auto read = b.getStringAttr(
"read");
166 APInt( 0, 0,
false));
167 auto i1 = b.getI1Type();
171 SmallVector<ServiceImplementConnReqOp, 8> toClientReqs(
172 llvm::make_filter_range(
173 implReq.getOps<ServiceImplementConnReqOp>(),
174 [](
auto req) { return req.getToClient() != nullptr; }));
177 DenseMap<Value, Value> outputMap;
178 for (
auto [bout, reqout] :
179 llvm::zip_longest(toClientReqs, implReq.getResults())) {
181 assert(reqout.has_value());
182 Value toClient = bout->getToClient();
183 outputMap[toClient] = *reqout;
187 hw::UnpackedArrayType memType =
190 b.create<
sv::RegOp>(memType, implReq.getServiceSymbolAttr().getAttr())
195 SmallVector<std::tuple<Value, Value, Value>> writeGoAddressData;
196 for (
auto req : implReq.getOps<ServiceImplementConnReqOp>()) {
197 auto port = req.getServicePort().getName();
204 auto doneValid = bb.
get(i1);
205 auto ackChannel = b.create<WrapValidReadyOp>(none, doneValid);
208 b.create<PackBundleOp>(implReq.getLoc(), req.getToClient().getType(),
209 ackChannel.getChanOutput());
211 pack.getFromChannels()[RandomAccessMemoryDeclOp::ReqDirChannelIdx];
212 toClientResp = pack.getBundle();
216 b.create<UnwrapValidReadyOp>(toServer, ackChannel.getReady());
219 b.getStringAttr(
"address"));
221 b.getStringAttr(
"data"));
225 go->setAttr(
"sv.namehint", b.getStringAttr(
"write_go"));
230 writeGoAddressData.push_back(std::make_tuple(go, address, data));
232 }
else if (port == read) {
236 auto dataValid = bb.
get(i1);
237 auto data = bb.
get(ramDecl.getInnerType());
238 auto dataChannel = b.create<WrapValidReadyOp>(data, dataValid);
241 b.create<PackBundleOp>(implReq.getLoc(), req.getToClient().getType(),
242 dataChannel.getChanOutput());
244 pack.getFromChannels()[RandomAccessMemoryDeclOp::RespDirChannelIdx];
245 toClientResp = pack.getBundle();
249 b.create<UnwrapValidReadyOp>(toServer, dataChannel.getReady());
251 b.create<sv::ArrayIndexInOutOp>(mem, addressUnwrap.getRawOutput());
255 data.setValue(readData);
256 dataValid.setValue(addressUnwrap.getValid());
258 assert(
false &&
"Port should be either 'read' or 'write'");
261 outputMap[req.getToClient()].replaceAllUsesWith(toClientResp);
265 auto hwClk = b.create<seq::FromClockOp>(clk);
266 b.create<sv::AlwaysFFOp>(
267 sv::EventControl::AtPosEdge, hwClk, ResetType::SyncReset,
268 sv::EventControl::AtPosEdge, rst, [&] {
269 for (
auto [go, address, data] : writeGoAddressData) {
270 Value a = address, d = data;
272 b.create<sv::IfOp>(go, [&] {
273 Value memLoc = b.create<sv::ArrayIndexInOutOp>(mem, a);
274 b.create<sv::PAssignOp>(memLoc, d);
284 DenseMap<StringRef, ServiceGeneratorDispatcher::ServiceGeneratorFunc>{
306 struct ESIConnectServicesPass
307 :
public ESIConnectServicesBase<ESIConnectServicesPass>,
311 : genDispatcher(gen) {}
312 ESIConnectServicesPass()
315 void runOnOperation()
override;
322 void convertReq(RequestToClientConnectionOp);
323 void convertReq(RequestToServerConnectionOp);
328 LogicalResult surfaceReqs(hw::HWMutableModuleLike,
329 ArrayRef<ServiceImplementConnReqOp>);
334 LogicalResult replaceInst(ServiceInstanceOp, Block *portReqs);
338 LogicalResult process(hw::HWModuleLike);
345 void ESIConnectServicesPass::runOnOperation() {
346 ModuleOp outerMod = getOperation();
347 topLevelSyms.addDefinitions(outerMod);
349 outerMod.walk([&](Operation *op) {
350 if (
auto req = dyn_cast<RequestToClientConnectionOp>(op))
352 else if (
auto req = dyn_cast<RequestToServerConnectionOp>(op))
360 SmallVector<hw::HWModuleLike, 64> sortedMods;
361 getAndSortModules(outerMod, sortedMods);
364 for (
auto mod : sortedMods) {
365 hw::HWModuleLike mutableMod = dyn_cast<hw::HWModuleLike>(*mod);
366 if (mutableMod && failed(process(mutableMod))) {
373 void ESIConnectServicesPass::convertReq(
374 RequestToClientConnectionOp toClientReq) {
375 OpBuilder b(toClientReq);
377 auto newReq = b.create<ServiceImplementConnReqOp>(
378 toClientReq.getLoc(), toClientReq.getToClient().getType(),
379 toClientReq.getServicePortAttr(),
381 newReq->setDialectAttrs(toClientReq->getDialectAttrs());
382 toClientReq.getToClient().replaceAllUsesWith(newReq.getToClient());
385 b.create<ServiceRequestRecordOp>(toClientReq.getLoc(), toClientReq.getAppID(),
386 toClientReq.getServicePortAttr(),
387 BundleDirection::toClient,
388 toClientReq.getToClient().getType());
392 void ESIConnectServicesPass::convertReq(
393 RequestToServerConnectionOp toServerReq) {
394 OpBuilder b(toServerReq);
399 ChannelBundleType toClientType =
400 toServerReq.getToServer().getType().getReversed();
403 auto toClientReq = b.create<ServiceImplementConnReqOp>(
404 toServerReq.getLoc(), toClientType, toServerReq.getServicePortAttr(),
406 toClientReq->setDialectAttrs(toServerReq->getDialectAttrs());
409 SmallVector<Value, 8> unpackToClientFromChannels;
410 SmallVector<Backedge, 8> unpackToClientFromChannelsBackedges;
412 if (ch.direction == ChannelDirection::to)
414 unpackToClientFromChannelsBackedges.push_back(beb.get(ch.type));
415 unpackToClientFromChannels.push_back(
416 unpackToClientFromChannelsBackedges.back());
418 auto unpackToClient =
419 b.create<UnpackBundleOp>(toServerReq.getLoc(), toClientReq.getToClient(),
420 unpackToClientFromChannels);
424 auto unpackToServer =
425 b.create<UnpackBundleOp>(toServerReq.getLoc(), toServerReq.getToServer(),
426 unpackToClient.getToChannels());
427 for (
auto [v, be] : llvm::zip_equal(unpackToServer.getToChannels(),
428 unpackToClientFromChannelsBackedges))
432 b.create<ServiceRequestRecordOp>(toServerReq.getLoc(), toServerReq.getAppID(),
433 toServerReq.getServicePortAttr(),
434 BundleDirection::toServer,
435 toServerReq.getToServer().getType());
439 LogicalResult ESIConnectServicesPass::process(hw::HWModuleLike mod) {
441 if (mod->getNumRegions() == 0 || mod->getRegion(0).empty())
444 Block &modBlock = mod->getRegion(0).front();
447 SmallVector<ServiceImplementConnReqOp, 4> nonLocalReqs;
449 DenseMap<SymbolRefAttr, Block *> localImplReqs;
450 Block *anyServiceInst =
nullptr;
451 for (
auto instOp : modBlock.getOps<ServiceInstanceOp>()) {
452 auto *b =
new Block();
453 localImplReqs[instOp.getServiceSymbolAttr()] = b;
454 if (!instOp.getServiceSymbolAttr())
459 mod.walk([&](ServiceImplementConnReqOp req) {
460 auto service = req.getServicePort().getModuleRef();
461 auto implOpF = localImplReqs.find(service);
462 if (implOpF != localImplReqs.end())
463 req->moveBefore(implOpF->second, implOpF->second->end());
464 else if (anyServiceInst)
465 req->moveBefore(anyServiceInst, anyServiceInst->end());
467 nonLocalReqs.push_back(req);
473 llvm::make_early_inc_range(modBlock.getOps<ServiceInstanceOp>())) {
474 Block *portReqs = localImplReqs[instOp.getServiceSymbolAttr()];
475 if (failed(replaceInst(instOp, portReqs)))
480 if (nonLocalReqs.empty())
483 if (
auto mutableMod = dyn_cast<hw::HWMutableModuleLike>(mod.getOperation()))
484 return surfaceReqs(mutableMod, nonLocalReqs);
485 return mod.emitOpError(
486 "Cannot surface requests through module without mutable ports");
489 LogicalResult ESIConnectServicesPass::replaceInst(ServiceInstanceOp instOp,
492 auto declSym = instOp.getServiceSymbolAttr();
493 ServiceDeclOpInterface decl;
495 decl = dyn_cast_or_null<ServiceDeclOpInterface>(
496 topLevelSyms.getDefinition(declSym));
498 return instOp.emitOpError(
"Could not find service declaration ")
504 SmallVector<Type, 8> resultTypes(instOp.getResultTypes().begin(),
505 instOp.getResultTypes().end());
506 for (
auto req : portReqs->getOps<ServiceImplementConnReqOp>())
507 resultTypes.push_back(req.getBundleType());
511 auto implOp = b.create<ServiceImplementReqOp>(
512 instOp.getLoc(), resultTypes, instOp.getAppIDAttr(),
513 instOp.getServiceSymbolAttr(), instOp.getImplTypeAttr(),
514 instOp.getImplOptsAttr(), instOp.getOperands());
515 implOp->setDialectAttrs(instOp->getDialectAttrs());
516 implOp.getPortReqs().push_back(portReqs);
519 for (
auto [n, o] : llvm::zip(implOp.getResults(), instOp.getResults()))
520 o.replaceAllUsesWith(n);
521 unsigned instOpNumResults = instOp.getNumResults();
522 for (
auto [idx, req] :
523 llvm::enumerate(portReqs->getOps<ServiceImplementConnReqOp>())) {
524 req.getToClient().replaceAllUsesWith(
525 implOp.getResult(idx + instOpNumResults));
529 if (failed(genDispatcher.generate(implOp, decl)))
530 return instOp.emitOpError(
"failed to generate server");
537 ESIConnectServicesPass::surfaceReqs(hw::HWMutableModuleLike mod,
538 ArrayRef<ServiceImplementConnReqOp> reqs) {
539 auto *ctxt = mod.getContext();
540 Block *body = &mod->getRegion(0).front();
543 unsigned origNumInputs = mod.getNumInputPorts();
544 SmallVector<std::pair<unsigned, hw::PortInfo>> newInputs;
547 auto getPortName = [&](ArrayAttr namePath) {
548 std::string portName;
549 llvm::raw_string_ostream nameOS(portName);
551 namePath.getAsRange<AppIDAttr>(), nameOS,
552 [&](AppIDAttr appid) {
553 nameOS << appid.getName().getValue();
554 if (appid.getIndex())
555 nameOS <<
"_" << appid.getIndex();
561 for (
auto req : reqs)
562 if (req->getParentWithTrait<OpTrait::IsIsolatedFromAbove>() != mod)
563 return req.emitOpError(
564 "Cannot surface requests through isolated from above ops");
567 for (
auto req : reqs) {
568 newInputs.push_back(std::make_pair(
570 hw::PortInfo{{getPortName(req.getRelativeAppIDPathAttr()),
577 Value replValue = body->addArgument(req.getBundleType(), req->getLoc());
578 req.getToClient().replaceAllUsesWith(replValue);
580 mod.insertPorts(newInputs, {});
583 auto prependNamePart = [&](ArrayAttr appIDPath, AppIDAttr appID) {
584 SmallVector<Attribute, 8> newAppIDPath;
585 newAppIDPath.push_back(appID);
586 newAppIDPath.append(appIDPath.begin(), appIDPath.end());
591 SmallVector<igraph::InstanceOpInterface, 1> newModuleInstantiations;
592 for (
auto inst : moduleInstantiations[mod]) {
596 SmallVector<Value, 16> newOperands;
597 for (
auto req : reqs) {
599 ArrayAttr appIDPath = req.getRelativeAppIDPathAttr();
600 if (
auto instAppID = dyn_cast_or_null<AppIDAttr>(
601 inst->getDiscardableAttr(AppIDAttr::AppIDAttrName)))
602 appIDPath = prependNamePart(appIDPath, instAppID);
605 auto clone = b.create<ServiceImplementConnReqOp>(
606 req.getLoc(), req.getToClient().getType(), req.getServicePortAttr(),
608 clone->setDialectAttrs(req->getDialectAttrs());
609 newOperands.push_back(clone.getToClient());
611 inst->insertOperands(inst->getNumOperands(), newOperands);
613 if (
auto hwInst = dyn_cast<hw::InstanceOp>(*inst))
614 hwInst.setArgNamesAttr(b.getArrayAttr(mod.getInputNames()));
619 for (
auto req : reqs)
624 std::unique_ptr<OperationPass<ModuleOp>>
626 return std::make_unique<ESIConnectServicesPass>();
assert(baseType &&"element must be base type")
static LogicalResult instantiateCosimEndpointOps(ServiceImplementReqOp implReq, ServiceDeclOpInterface)
The generator for the "cosim" impl_type.
static LogicalResult instantiateSystemVerilogMemory(ServiceImplementReqOp implReq, ServiceDeclOpInterface decl)
static ServiceGeneratorDispatcher globalDispatcher(DenseMap< StringRef, ServiceGeneratorDispatcher::ServiceGeneratorFunc >{ {"cosim", instantiateCosimEndpointOps}, {"sv_mem", instantiateSystemVerilogMemory}}, false)
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.
std::function< LogicalResult(ServiceImplementReqOp, ServiceDeclOpInterface)> ServiceGeneratorFunc
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
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()
This file defines an intermediate representation for circuits acting as an abstraction for constraint...
A set of methods which are broadly useful in a number of dialects.