diff --git a/include/circt/Dialect/ESI/ESIServices.td b/include/circt/Dialect/ESI/ESIServices.td index 1c5aa05722f0..f1b131c2d847 100644 --- a/include/circt/Dialect/ESI/ESIServices.td +++ b/include/circt/Dialect/ESI/ESIServices.td @@ -166,7 +166,7 @@ def ServiceHierarchyMetadataOp : ESI_Op<"service.hierarchy.metadata", [ let summary = "Metadata about a service in the service hierarchy"; let arguments = (ins FlatSymbolRefAttr:$service_symbol, - StrArrayAttr:$serverNamePath, + ArrayAttr:$serverNamePath, StrAttr:$impl_type, OptionalAttr:$impl_details, ArrayAttr:$clients); diff --git a/lib/Dialect/ESI/ESIPasses.cpp b/lib/Dialect/ESI/ESIPasses.cpp index 28070d64a962..057233c7074f 100644 --- a/lib/Dialect/ESI/ESIPasses.cpp +++ b/lib/Dialect/ESI/ESIPasses.cpp @@ -1410,7 +1410,28 @@ static llvm::json::Value toJSON(Attribute attr) { return TypeSwitch(attr) .Case([&](StringAttr a) { return a.getValue(); }) .Case([&](IntegerAttr a) { return a.getValue().getLimitedValue(); }) - .Case([&](TypeAttr a) { return toJSON(a.getValue()); }) + .Case([&](TypeAttr a) { + Type t = a.getValue(); + llvm::json::Object typeMD; + typeMD["type_desc"] = toJSON(t); + + std::string buf; + llvm::raw_string_ostream(buf) << t; + typeMD["mlir_name"] = buf; + + if (auto chanType = t.dyn_cast()) { + Type inner = chanType.getInner(); + typeMD["hw_bitwidth"] = hw::getBitWidth(inner); +#ifdef CAPNP + capnp::TypeSchema schema(inner); + typeMD["capnp_type_id"] = schema.capnpTypeID(); + typeMD["capnp_name"] = schema.name(); +#endif + } else { + typeMD["hw_bitwidth"] = hw::getBitWidth(t); + } + return typeMD; + }) .Case([&](ArrayAttr a) { return llvm::json::Array( llvm::map_range(a, [](Attribute a) { return toJSON(a); })); @@ -1421,6 +1442,12 @@ static llvm::json::Value toJSON(Attribute attr) { dict[entry.getName().getValue()] = toJSON(entry.getValue()); return dict; }) + .Case([&](InnerRefAttr ref) { + llvm::json::Object dict; + dict["outer_sym"] = ref.getModule().getValue(); + dict["inner"] = ref.getName().getValue(); + return dict; + }) .Default([&](Attribute a) { std::string buff; llvm::raw_string_ostream(buff) << a; @@ -1451,45 +1478,21 @@ void ESIEmitCollateralPass::emitServiceJSON() { // Emit the list of ports of a service declaration. auto emitPorts = [&](ServiceDeclOp decl) { - j.array([&] { - for (auto *portOp : llvm::make_pointer_range(decl.ports().getOps())) { - j.object([&] { - if (auto port = dyn_cast(portOp)) { - j.attribute("name", port.inner_sym()); - j.attribute("to-server-type", toJSON(port.type())); - } else if (auto port = dyn_cast(portOp)) { - j.attribute("name", port.inner_sym()); - j.attribute("to-client-type", toJSON(port.type())); - } else if (auto port = dyn_cast(portOp)) { - j.attribute("name", port.inner_sym()); - j.attribute("to-client-type", toJSON(port.outType())); - j.attribute("to-server-type", toJSON(port.inType())); - } - }); - } - }); - }; - - auto emitServicesForModule = [&](Operation *hwMod) { - // Emit a list of the servers in a design and the clients connected to them. - j.attributeArray("services", [&] { - hwMod->walk([&](ServiceHierarchyMetadataOp metadata) { - j.object([&] { - j.attribute("service", metadata.service_symbol()); - j.attributeArray("path", [&] { - for (auto attr : metadata.serverNamePath()) - j.value(attr.cast().getValue()); - }); - j.attribute("impl_type", metadata.impl_type()); - if (metadata.impl_detailsAttr()) - j.attribute("impl_details", toJSON(metadata.impl_detailsAttr())); - j.attributeArray("clients", [&] { - for (auto client : metadata.clients()) - j.value(toJSON(client)); - }); - }); + for (auto *portOp : llvm::make_pointer_range(decl.ports().getOps())) { + j.object([&] { + if (auto port = dyn_cast(portOp)) { + j.attribute("name", port.inner_sym()); + j.attribute("to-server-type", toJSON(port.type())); + } else if (auto port = dyn_cast(portOp)) { + j.attribute("name", port.inner_sym()); + j.attribute("to-client-type", toJSON(port.type())); + } else if (auto port = dyn_cast(portOp)) { + j.attribute("name", port.inner_sym()); + j.attribute("to-client-type", toJSON(port.outType())); + j.attribute("to-server-type", toJSON(port.inType())); + } }); - }); + } }; j.object([&] { @@ -1511,7 +1514,51 @@ void ESIEmitCollateralPass::emitServiceJSON() { auto sym = FlatSymbolRefAttr::get(ctxt, topModName); Operation *hwMod = topSyms.getDefinition(sym); j.attribute("module", toJSON(sym)); - emitServicesForModule(hwMod); + j.attributeArray("services", [&] { + hwMod->walk([&](ServiceHierarchyMetadataOp md) { + j.object([&] { + j.attribute("service", md.service_symbol()); + j.attribute("instance_path", toJSON(md.serverNamePathAttr())); + }); + }); + }); + }); + } + }); + + // Get a list of metadata ops which originated in modules (path is empty). + DenseMap> + modsWithLocalServices; + for (auto hwmod : mod.getOps()) { + SmallVector metadataOps; + hwmod.walk([&metadataOps](ServiceHierarchyMetadataOp md) { + if (md.serverNamePath().empty()) + metadataOps.push_back(md); + }); + if (!metadataOps.empty()) + modsWithLocalServices[hwmod] = metadataOps; + } + + // Then output metadata for those modules exclusively. + j.attributeArray("modules", [&] { + for (auto &modWithSvc : modsWithLocalServices) { + j.object([&] { + j.attribute("symbol", modWithSvc.first.moduleName()); + j.attributeArray("services", [&] { + for (ServiceHierarchyMetadataOp metadata : modWithSvc.getSecond()) { + j.object([&] { + j.attribute("service", metadata.service_symbol()); + j.attribute("impl_type", metadata.impl_type()); + if (metadata.impl_detailsAttr()) + j.attribute("impl_details", + toJSON(metadata.impl_detailsAttr())); + j.attributeArray("clients", [&] { + for (auto client : metadata.clients()) + j.value(toJSON(client)); + }); + }); + } + }); }); } }); diff --git a/lib/Dialect/ESI/ESIServices.cpp b/lib/Dialect/ESI/ESIServices.cpp index 910ca5050f32..1041e6a5a8a2 100644 --- a/lib/Dialect/ESI/ESIServices.cpp +++ b/lib/Dialect/ESI/ESIServices.cpp @@ -310,7 +310,9 @@ void ESIConnectServicesPass::copyMetadata(hw::HWMutableModuleLike mod) { auto instName = b.getStringAttr(inst.instanceName()); for (auto metadata : metadataOps) { SmallVector path; - path.push_back(instName); + path.push_back(hw::InnerRefAttr::get( + cast(mod.getOperation()).moduleNameAttr(), + instName)); for (auto attr : metadata.serverNamePathAttr()) path.push_back(attr); diff --git a/test/Dialect/ESI/services_collateral.mlir b/test/Dialect/ESI/services_collateral.mlir index 9789336d39e8..2e5483681636 100644 --- a/test/Dialect/ESI/services_collateral.mlir +++ b/test/Dialect/ESI/services_collateral.mlir @@ -5,51 +5,50 @@ // CHECK-LABEL: "declarations": [ -// CHECK-LABEL: "name": "HostComms" -// CHECK: "ports": [ -// CHECK: [ -// CHECK: { -// CHECK: "name": "Send", -// CHECK: "to-server-type": { -// CHECK: "dialect": "esi", -// CHECK: "inner": { -// CHECK: "dialect": "esi", -// CHECK: "mnemonic": "any" -// CHECK: }, -// CHECK: "mnemonic": "channel" -// CHECK: } -// CHECK: }, -// CHECK: { -// CHECK: "name": "Recv", -// CHECK: "to-client-type": { -// CHECK: "dialect": "esi", -// CHECK: "inner": { -// CHECK: "dialect": "builtin", -// CHECK: "mnemonic": "i8" -// CHECK: }, -// CHECK: "mnemonic": "channel" -// CHECK: } -// CHECK: }, -// CHECK: { -// CHECK: "name": "ReqResp", -// CHECK: "to-client-type": { -// CHECK: "dialect": "esi", -// CHECK: "inner": { -// CHECK: "dialect": "builtin", -// CHECK: "mnemonic": "i16" -// CHECK: }, -// CHECK: "mnemonic": "channel" -// CHECK: }, -// CHECK: "to-server-type": { -// CHECK: "dialect": "esi", -// CHECK: "inner": { -// CHECK: "dialect": "builtin", -// CHECK: "mnemonic": "i8" -// CHECK: }, -// CHECK: "mnemonic": "channel" -// CHECK: } -// CHECK: } -// CHECK: ] +// CHECK-LABEL: "name": "HostComms", +// CHECK-NEXT: "ports": [ +// CHECK-NEXT: { +// CHECK-NEXT: "name": "Send", +// CHECK-NEXT: "to-server-type": { +// CHECK-NEXT: "dialect": "esi", +// CHECK-NEXT: "inner": { +// CHECK-NEXT: "dialect": "esi", +// CHECK-NEXT: "mnemonic": "any" +// CHECK-NEXT: }, +// CHECK-NEXT: "mnemonic": "channel" +// CHECK-NEXT: } +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "name": "Recv", +// CHECK-NEXT: "to-client-type": { +// CHECK-NEXT: "dialect": "esi", +// CHECK-NEXT: "inner": { +// CHECK-NEXT: "dialect": "builtin", +// CHECK-NEXT: "mnemonic": "i8" +// CHECK-NEXT: }, +// CHECK-NEXT: "mnemonic": "channel" +// CHECK-NEXT: } +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "name": "ReqResp", +// CHECK-NEXT: "to-client-type": { +// CHECK-NEXT: "dialect": "esi", +// CHECK-NEXT: "inner": { +// CHECK-NEXT: "dialect": "builtin", +// CHECK-NEXT: "mnemonic": "i16" +// CHECK-NEXT: }, +// CHECK-NEXT: "mnemonic": "channel" +// CHECK-NEXT: }, +// CHECK-NEXT: "to-server-type": { +// CHECK-NEXT: "dialect": "esi", +// CHECK-NEXT: "inner": { +// CHECK-NEXT: "dialect": "builtin", +// CHECK-NEXT: "mnemonic": "i8" +// CHECK-NEXT: }, +// CHECK-NEXT: "mnemonic": "channel" +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: ] esi.service.decl @HostComms { esi.service.to_server @Send : !esi.channel esi.service.to_client @Recv : !esi.channel @@ -73,42 +72,77 @@ msft.module @LoopbackCosimTop {} (%clk: i1, %rst: i1) { // CHECK-LABEL: "top_levels": [ // CHECK-LABEL: "module": "@LoopbackCosimTopWrapper", -// CHECK: "services": [ -// CHECK: { -// CHECK: "service": "HostComms", -// CHECK: "path": [ -// CHECK: "top" -// CHECK: ], -// CHECK: "impl_type": "cosim", -// CHECK: "clients": [ -// CHECK: { -// CHECK: "client_name": [ -// CHECK: "m1", -// CHECK: "loopback_inout" -// CHECK: ], -// CHECK: "port": "#hw.innerNameRef<@HostComms::@ReqResp>", -// CHECK: "to_client_type": { -// CHECK: "dialect": "esi", -// CHECK: "inner": { -// CHECK: "dialect": "builtin", -// CHECK: "mnemonic": "i16" -// CHECK: }, -// CHECK: "mnemonic": "channel" -// CHECK: } -// CHECK: }, -// CHECK: { -// CHECK: "client_name": [ -// CHECK: "m1", -// CHECK: "loopback_inout" -// CHECK: ], -// CHECK: "port": "#hw.innerNameRef<@HostComms::@ReqResp>", -// CHECK: "to_server_type": { -// CHECK: "dialect": "esi", -// CHECK: "inner": { -// CHECK: "dialect": "builtin", -// CHECK: "mnemonic": "i8" -// CHECK: }, -// CHECK: "mnemonic": "channel" +// CHECK-NEXT: "services": [ +// CHECK-NEXT: { +// CHECK-NEXT: "service": "HostComms", +// CHECK-NEXT: "instance_path": [ +// CHECK-NEXT: { +// CHECK-NEXT: "inner": "top", +// CHECK-NEXT: "outer_sym": "LoopbackCosimTop" +// CHECK-NEXT: } +// CHECK-NEXT: ] + +// CHECK-LABEL: "modules": [ +// CHECK-NEXT: { +// CHECK-NEXT: "symbol": "LoopbackCosimTop", +// CHECK-NEXT: "services": [ +// CHECK-NEXT: { +// CHECK-NEXT: "service": "HostComms", +// CHECK-NEXT: "impl_type": "cosim", +// CHECK-NEXT: "clients": [ +// CHECK-NEXT: { +// CHECK-NEXT: "client_name": [ +// CHECK-NEXT: "m1", +// CHECK-NEXT: "loopback_inout" +// CHECK-NEXT: ], +// CHECK-NEXT: "port": { +// CHECK-NEXT: "inner": "ReqResp", +// CHECK-NEXT: "outer_sym": "HostComms" +// CHECK-NEXT: }, +// CHECK-NEXT: "to_client_type": { +// CHECK-NEXT: "capnp_name": "I16", +// CHECK-NEXT: "capnp_type_id": 15002640976408729367, +// CHECK-NEXT: "hw_bitwidth": 16, +// CHECK-NEXT: "mlir_name": "!esi.channel", +// CHECK-NEXT: "type_desc": { +// CHECK-NEXT: "dialect": "esi", +// CHECK-NEXT: "inner": { +// CHECK-NEXT: "dialect": "builtin", +// CHECK-NEXT: "mnemonic": "i16" +// CHECK-NEXT: }, +// CHECK-NEXT: "mnemonic": "channel" +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "client_name": [ +// CHECK-NEXT: "m1", +// CHECK-NEXT: "loopback_inout" +// CHECK-NEXT: ], +// CHECK-NEXT: "port": { +// CHECK-NEXT: "inner": "ReqResp", +// CHECK-NEXT: "outer_sym": "HostComms" +// CHECK-NEXT: }, +// CHECK-NEXT: "to_server_type": { +// CHECK-NEXT: "capnp_name": "I8", +// CHECK-NEXT: "capnp_type_id": 9950424317211852587, +// CHECK-NEXT: "hw_bitwidth": 8, +// CHECK-NEXT: "mlir_name": "!esi.channel", +// CHECK-NEXT: "type_desc": { +// CHECK-NEXT: "dialect": "esi", +// CHECK-NEXT: "inner": { +// CHECK-NEXT: "dialect": "builtin", +// CHECK-NEXT: "mnemonic": "i8" +// CHECK-NEXT: }, +// CHECK-NEXT: "mnemonic": "channel" +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: ] msft.module @LoopbackCosimTopWrapper {} (%clk: i1, %rst: i1) {