Skip to content

Commit

Permalink
[ESI] Promote and generalize 'channel assignments' (#7715)
Browse files Browse the repository at this point in the history
Previously, cosim store its ESI channel to cosim channel name in the
`implDetails`. DMA engines to make channel communication work in real
hardware need this information as well.
  • Loading branch information
teqdruid authored Oct 18, 2024
1 parent ffc954a commit 313ac7a
Show file tree
Hide file tree
Showing 10 changed files with 104 additions and 52 deletions.
4 changes: 2 additions & 2 deletions frontends/PyCDE/src/pycde/bsp/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,15 +152,15 @@ def build_table(bundles) -> Tuple[Dict[int, AssignableSignal], int]:
for bundle in bundles.to_client_reqs:
if bundle.port == 'read':
table[offset] = bundle
bundle.add_record({
bundle.add_record(details={
"offset": offset,
"size": ChannelMMIO.RegisterSpace,
"type": "ro"
})
offset += ChannelMMIO.RegisterSpace
elif bundle.port == 'read_write':
table[offset] = bundle
bundle.add_record({
bundle.add_record(details={
"offset": offset,
"size": ChannelMMIO.RegisterSpace,
"type": "rw"
Expand Down
15 changes: 9 additions & 6 deletions frontends/PyCDE/src/pycde/esi.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from .module import generator, Module, ModuleLikeBuilderBase, PortProxyBase
from .signals import (BitsSignal, BundleSignal, ChannelSignal, Signal,
_FromCirctValue)
from .support import _obj_to_attribute, get_user_loc, obj_to_typed_attribute
from .support import optional_dict_to_dict_attr, get_user_loc
from .system import System
from .types import (Any, Bits, Bundle, BundledChannel, Channel,
ChannelDirection, StructType, Type, UInt, types,
Expand Down Expand Up @@ -140,21 +140,24 @@ def __init__(self, req: raw_esi.ServiceImplementConnReqOp,
self.port = hw.InnerRefAttr(req.servicePort).name.value
self._bundle_to_replace: Optional[ir.OpResult] = old_value_to_replace

def add_record(self, details: Dict[str, object]):
def add_record(self,
channel_assignments: Optional[Dict] = None,
details: Optional[Dict[str, object]] = None):
"""Add a record to the manifest for this client request. Generally used to
give the runtime necessary information about how to connect to the client
through the generated service. For instance, offsets into an MMIO space."""

ir_details: Dict[str, ir.Attribute] = {}
for k, v in details.items():
ir_details[k] = _obj_to_attribute(v)
channel_assignments = optional_dict_to_dict_attr(channel_assignments)
details = optional_dict_to_dict_attr(details)

with get_user_loc(), ir.InsertionPoint.at_block_begin(
self.rec.reqDetails.blocks[0]):
raw_esi.ServiceImplClientRecordOp(
self.req.relativeAppIDPath,
self.req.servicePort,
ir.TypeAttr.get(self.req.toClient.type),
ir_details,
channelAssignments=channel_assignments,
implDetails=details,
)

@property
Expand Down
9 changes: 6 additions & 3 deletions include/circt/Dialect/ESI/ESIManifest.td
Original file line number Diff line number Diff line change
Expand Up @@ -123,16 +123,19 @@ def ServiceImplClientRecordOp : ESI_Op<"manifest.impl_conn", [
let summary = "Details of a service implementation client connection";
let description = [{
A record containing all the necessary details of how to connect to a client
which the parent service record is servicing.
which the parent service record is servicing. Emitted on a per-client bundle
basis. There shall be at most on of these records in the entire manifest for
a particular client.
}];

let arguments = (ins AppIDArrayAttr:$relAppIDPath,
InnerRefAttr:$servicePort,
TypeAttrOf<ChannelBundleType>:$typeID,
DictionaryAttr:$implDetails);
OptionalAttr<DictionaryAttr>:$channelAssignments,
OptionalAttr<DictionaryAttr>:$implDetails);
let assemblyFormat = [{
$relAppIDPath `req` $servicePort `(` $typeID `)`
`with` $implDetails attr-dict
(`channels` $channelAssignments^)? (`with` $implDetails^)? attr-dict
}];

let extraClassDeclaration = [{
Expand Down
8 changes: 6 additions & 2 deletions lib/Dialect/ESI/ESIOps.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -687,9 +687,13 @@ void ServiceImplClientRecordOp::getDetails(
NamedAttribute(StringAttr::get(getContext(), "port"),
servicePort.getName()),
}));
if (const auto &channelAssignments = getChannelAssignments())
results.push_back(
NamedAttribute(getChannelAssignmentsAttrName(), *channelAssignments));
// Don't add another level for the implementation details.
for (auto implDetail : getImplDetailsAttr().getValue())
results.push_back(implDetail);
if (const auto &implDetails = getImplDetails())
for (const auto &implDetail : *implDetails)
results.push_back(implDetail);
}

StringRef ServiceRequestRecordOp::getManifestClass() { return "clientPort"; }
Expand Down
19 changes: 12 additions & 7 deletions lib/Dialect/ESI/ESIServices.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,14 @@ instantiateCosimEndpointOps(ServiceImplementReqOp implReq,
return StringAttr::get(ctxt, os.str());
};

auto getAssignment = [&](StringAttr name, StringAttr channelName) {
DictionaryAttr assignment = b.getDictionaryAttr({
b.getNamedAttr("type", b.getStringAttr("cosim")),
b.getNamedAttr("name", channelName),
});
return b.getNamedAttr(name, assignment);
};

llvm::DenseMap<ServiceImplementConnReqOp, unsigned> toClientResultNum;
for (auto req : implReq.getOps<ServiceImplementConnReqOp>())
toClientResultNum[req] = toClientResultNum.size();
Expand All @@ -97,8 +105,7 @@ instantiateCosimEndpointOps(ServiceImplementReqOp implReq,
loc, ch.type, clk, rst,
toStringAttr(req.getRelativeAppIDPathAttr(), ch.name));
toServerValues.push_back(cosim.getFromHost());
channelAssignments.push_back(
b.getNamedAttr(ch.name, cosim.getIdAttr()));
channelAssignments.push_back(getAssignment(ch.name, cosim.getIdAttr()));
}
}

Expand All @@ -113,16 +120,14 @@ instantiateCosimEndpointOps(ServiceImplementReqOp implReq,
auto cosim = b.create<CosimToHostEndpointOp>(
loc, clk, rst, pack.getFromChannels()[chanIdx++],
toStringAttr(req.getRelativeAppIDPathAttr(), ch.name));
channelAssignments.push_back(
b.getNamedAttr(ch.name, cosim.getIdAttr()));
channelAssignments.push_back(getAssignment(ch.name, cosim.getIdAttr()));
}
}

implRecords.create<ServiceImplClientRecordOp>(
req.getLoc(), req.getRelativeAppIDPathAttr(), req.getServicePortAttr(),
TypeAttr::get(bundleType),
b.getDictionaryAttr(b.getNamedAttr(
"channel_assignments", b.getDictionaryAttr(channelAssignments))));
TypeAttr::get(bundleType), b.getDictionaryAttr(channelAssignments),
DictionaryAttr());
}

// Erase the generation request.
Expand Down
11 changes: 11 additions & 0 deletions lib/Dialect/ESI/runtime/cpp/include/esi/Common.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,22 @@ struct ServicePortDesc {
std::string portName;
};

/// Details about how to connect to a particular channel.
struct ChannelAssignment {
/// The name of the type of connection. Typically, the name of the DMA engine
/// or "cosim" if a cosimulation channel is being used.
std::string type;
/// Implementation-specific options.
std::map<std::string, std::any> implOptions;
};
using ChannelAssignments = std::map<std::string, ChannelAssignment>;

/// A description of a hardware client. Used pretty exclusively in setting up
/// the design.
struct HWClientDetail {
AppIDPath relPath;
ServicePortDesc port;
ChannelAssignments channelAssignments;
std::map<std::string, std::any> implOptions;
};
using HWClientDetails = std::vector<HWClientDetail>;
Expand Down
12 changes: 11 additions & 1 deletion lib/Dialect/ESI/runtime/cpp/lib/Manifest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,17 @@ Manifest::Impl::getService(AppIDPath idPath, AcceleratorConnection &acc,
clientDetail.relPath = parseIDPath(detail.value());
else if (detail.key() == "servicePort")
clientDetail.port = parseServicePort(detail.value());
else
else if (detail.key() == "channelAssignments") {
for (auto &chan : detail.value().items()) {
ChannelAssignment chanAssign;
for (auto &assign : chan.value().items())
if (assign.key() == "type")
chanAssign.type = assign.value();
else
chanAssign.implOptions[assign.key()] = getAny(assign.value());
clientDetail.channelAssignments[chan.key()] = chanAssign;
}
} else
clientDetail.implOptions[detail.key()] = getAny(detail.value());
}
clientDetails.push_back(clientDetail);
Expand Down
20 changes: 9 additions & 11 deletions lib/Dialect/ESI/runtime/cpp/lib/backends/Cosim.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -455,17 +455,15 @@ Service *CosimAccelerator::createService(Service::Type svcType,
if (prefix.size() > 0)
prefix.pop_back();

if (implName == "cosim") {
// Get the channel assignments for each client.
for (auto client : clients) {
AppIDPath fullClientPath = prefix + client.relPath;
std::map<std::string, std::string> channelAssignments;
for (auto assignment : std::any_cast<std::map<std::string, std::any>>(
client.implOptions.at("channel_assignments")))
channelAssignments[assignment.first] =
std::any_cast<std::string>(assignment.second);
clientChannelAssignments[fullClientPath] = std::move(channelAssignments);
}
// Get the channel assignments for each client.
for (auto client : clients) {
AppIDPath fullClientPath = prefix + client.relPath;
std::map<std::string, std::string> channelAssignments;
for (auto assignment : client.channelAssignments)
if (assignment.second.type == "cosim")
channelAssignments[assignment.first] = std::any_cast<std::string>(
assignment.second.implOptions.at("name"));
clientChannelAssignments[fullClientPath] = std::move(channelAssignments);
}

if (svcType == typeid(services::MMIO)) {
Expand Down
54 changes: 36 additions & 18 deletions test/Dialect/ESI/manifest.mlir
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,12 @@ hw.module @top(in %clk: !seq.clock, in %rst: i1) {
// HIER-LABEL: esi.manifest.compressed <"{{.+}}">
// HIER-LABEL: esi.manifest.hier_root @top {
// HIER-NEXT: esi.manifest.service_impl #esi.appid<"cosim"> svc @HostComms by "cosim" with {} {
// HIER-NEXT: esi.manifest.impl_conn [#esi.appid<"loopback_inst"[0]>, #esi.appid<"loopback_tohw">] req <@HostComms::@Recv>(!esi.bundle<[!esi.channel<i8> to "recv"]>) with {channel_assignments = {recv = "loopback_inst[0].loopback_tohw.recv"}}
// HIER-NEXT: esi.manifest.impl_conn [#esi.appid<"loopback_inst"[0]>, #esi.appid<"loopback_fromhw">] req <@HostComms::@Send>(!esi.bundle<[!esi.channel<i8> from "send"]>) with {channel_assignments = {send = "loopback_inst[0].loopback_fromhw.send"}}
// HIER-NEXT: esi.manifest.impl_conn [#esi.appid<"loopback_inst"[0]>, #esi.appid<"loopback_fromhw_i0">] req <@HostComms::@SendI0>(!esi.bundle<[!esi.channel<i0> from "send"]>) with {channel_assignments = {send = "loopback_inst[0].loopback_fromhw_i0.send"}}
// HIER-NEXT: esi.manifest.impl_conn [#esi.appid<"loopback_inst"[1]>, #esi.appid<"loopback_tohw">] req <@HostComms::@Recv>(!esi.bundle<[!esi.channel<i8> to "recv"]>) with {channel_assignments = {recv = "loopback_inst[1].loopback_tohw.recv"}}
// HIER-NEXT: esi.manifest.impl_conn [#esi.appid<"loopback_inst"[1]>, #esi.appid<"loopback_fromhw">] req <@HostComms::@Send>(!esi.bundle<[!esi.channel<i8> from "send"]>) with {channel_assignments = {send = "loopback_inst[1].loopback_fromhw.send"}}
// HIER-NEXT: esi.manifest.impl_conn [#esi.appid<"loopback_inst"[1]>, #esi.appid<"loopback_fromhw_i0">] req <@HostComms::@SendI0>(!esi.bundle<[!esi.channel<i0> from "send"]>) with {channel_assignments = {send = "loopback_inst[1].loopback_fromhw_i0.send"}}
// HIER-NEXT: esi.manifest.impl_conn [#esi.appid<"loopback_inst"[0]>, #esi.appid<"loopback_tohw">] req <@HostComms::@Recv>(!esi.bundle<[!esi.channel<i8> to "recv"]>) channels {recv = {name = "loopback_inst[0].loopback_tohw.recv", type = "cosim"}}
// HIER-NEXT: esi.manifest.impl_conn [#esi.appid<"loopback_inst"[0]>, #esi.appid<"loopback_fromhw">] req <@HostComms::@Send>(!esi.bundle<[!esi.channel<i8> from "send"]>) channels {send = {name = "loopback_inst[0].loopback_fromhw.send", type = "cosim"}}
// HIER-NEXT: esi.manifest.impl_conn [#esi.appid<"loopback_inst"[0]>, #esi.appid<"loopback_fromhw_i0">] req <@HostComms::@SendI0>(!esi.bundle<[!esi.channel<i0> from "send"]>) channels {send = {name = "loopback_inst[0].loopback_fromhw_i0.send", type = "cosim"}}
// HIER-NEXT: esi.manifest.impl_conn [#esi.appid<"loopback_inst"[1]>, #esi.appid<"loopback_tohw">] req <@HostComms::@Recv>(!esi.bundle<[!esi.channel<i8> to "recv"]>) channels {recv = {name = "loopback_inst[1].loopback_tohw.recv", type = "cosim"}}
// HIER-NEXT: esi.manifest.impl_conn [#esi.appid<"loopback_inst"[1]>, #esi.appid<"loopback_fromhw">] req <@HostComms::@Send>(!esi.bundle<[!esi.channel<i8> from "send"]>) channels {send = {name = "loopback_inst[1].loopback_fromhw.send", type = "cosim"}}
// HIER-NEXT: esi.manifest.impl_conn [#esi.appid<"loopback_inst"[1]>, #esi.appid<"loopback_fromhw_i0">] req <@HostComms::@SendI0>(!esi.bundle<[!esi.channel<i0> from "send"]>) channels {send = {name = "loopback_inst[1].loopback_fromhw_i0.send", type = "cosim"}}
// HIER-NEXT: }
// HIER-NEXT: esi.manifest.hier_node #esi.appid<"loopback_inst"[0]> mod @Loopback {
// HIER-NEXT: esi.manifest.req #esi.appid<"loopback_tohw">, <@HostComms::@Recv>, !esi.bundle<[!esi.channel<i8> to "recv"]>
Expand Down Expand Up @@ -120,8 +120,11 @@ hw.module @top(in %clk: !seq.clock, in %rst: i1) {
// CHECK-NEXT: "serviceImplName": "cosim",
// CHECK-NEXT: "clientDetails": [
// CHECK-NEXT: {
// CHECK-NEXT: "channel_assignments": {
// CHECK-NEXT: "recv": "loopback_inst[0].loopback_tohw.recv"
// CHECK-NEXT: "channelAssignments": {
// CHECK-NEXT: "recv": {
// CHECK-NEXT: "name": "loopback_inst[0].loopback_tohw.recv",
// CHECK-NEXT: "type": "cosim"
// CHECK-NEXT: }
// CHECK-NEXT: },
// CHECK-NEXT: "relAppIDPath": [
// CHECK-NEXT: {
Expand All @@ -138,8 +141,11 @@ hw.module @top(in %clk: !seq.clock, in %rst: i1) {
// CHECK-NEXT: }
// CHECK-NEXT: },
// CHECK-NEXT: {
// CHECK-NEXT: "channel_assignments": {
// CHECK-NEXT: "send": "loopback_inst[0].loopback_fromhw.send"
// CHECK-NEXT: "channelAssignments": {
// CHECK-NEXT: "send": {
// CHECK-NEXT: "name": "loopback_inst[0].loopback_fromhw.send",
// CHECK-NEXT: "type": "cosim"
// CHECK-NEXT: }
// CHECK-NEXT: },
// CHECK-NEXT: "relAppIDPath": [
// CHECK-NEXT: {
Expand All @@ -156,8 +162,11 @@ hw.module @top(in %clk: !seq.clock, in %rst: i1) {
// CHECK-NEXT: }
// CHECK-NEXT: },
// CHECK-NEXT: {
// CHECK-NEXT: "channel_assignments": {
// CHECK-NEXT: "send": "loopback_inst[0].loopback_fromhw_i0.send"
// CHECK-NEXT: "channelAssignments": {
// CHECK-NEXT: "send": {
// CHECK-NEXT: "name": "loopback_inst[0].loopback_fromhw_i0.send",
// CHECK-NEXT: "type": "cosim"
// CHECK-NEXT: }
// CHECK-NEXT: },
// CHECK-NEXT: "relAppIDPath": [
// CHECK-NEXT: {
Expand All @@ -174,8 +183,11 @@ hw.module @top(in %clk: !seq.clock, in %rst: i1) {
// CHECK-NEXT: }
// CHECK-NEXT: },
// CHECK-NEXT: {
// CHECK-NEXT: "channel_assignments": {
// CHECK-NEXT: "recv": "loopback_inst[1].loopback_tohw.recv"
// CHECK-NEXT: "channelAssignments": {
// CHECK-NEXT: "recv": {
// CHECK-NEXT: "name": "loopback_inst[1].loopback_tohw.recv",
// CHECK-NEXT: "type": "cosim"
// CHECK-NEXT: }
// CHECK-NEXT: },
// CHECK-NEXT: "relAppIDPath": [
// CHECK-NEXT: {
Expand All @@ -192,8 +204,11 @@ hw.module @top(in %clk: !seq.clock, in %rst: i1) {
// CHECK-NEXT: }
// CHECK-NEXT: },
// CHECK-NEXT: {
// CHECK-NEXT: "channel_assignments": {
// CHECK-NEXT: "send": "loopback_inst[1].loopback_fromhw.send"
// CHECK-NEXT: "channelAssignments": {
// CHECK-NEXT: "send": {
// CHECK-NEXT: "name": "loopback_inst[1].loopback_fromhw.send",
// CHECK-NEXT: "type": "cosim"
// CHECK-NEXT: }
// CHECK-NEXT: },
// CHECK-NEXT: "relAppIDPath": [
// CHECK-NEXT: {
Expand All @@ -210,8 +225,11 @@ hw.module @top(in %clk: !seq.clock, in %rst: i1) {
// CHECK-NEXT: }
// CHECK-NEXT: },
// CHECK-NEXT: {
// CHECK-NEXT: "channel_assignments": {
// CHECK-NEXT: "send": "loopback_inst[1].loopback_fromhw_i0.send"
// CHECK-NEXT: "channelAssignments": {
// CHECK-NEXT: "send": {
// CHECK-NEXT: "name": "loopback_inst[1].loopback_fromhw_i0.send",
// CHECK-NEXT: "type": "cosim"
// CHECK-NEXT: }
// CHECK-NEXT: },
// CHECK-NEXT: "relAppIDPath": [
// CHECK-NEXT: {
Expand Down
4 changes: 2 additions & 2 deletions test/Dialect/ESI/services.mlir
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ hw.module @InOutLoopback (in %clk: !seq.clock) {
// CONN-NEXT: [[clk:%.+]] = esi.pure_module.input "clk" : !seq.clock
// CONN-NEXT: [[rst:%.+]] = esi.pure_module.input "rst" : i1
// CONN-NEXT: esi.manifest.service_impl #esi.appid<"cosim"> svc @HostComms by "cosim" with {} {
// CONN-NEXT: esi.manifest.impl_conn [#esi.appid<"loopback_inout">] req <@HostComms::@ReqResp>(!esi.bundle<[!esi.channel<i16> to "req", !esi.channel<i8> from "resp"]>) with {channel_assignments = {req = "loopback_inout.req", resp = "loopback_inout.resp"}}
// CONN-NEXT: esi.manifest.impl_conn [#esi.appid<"loopback_inout">] req <@HostComms::@ReqResp>(!esi.bundle<[!esi.channel<i16> to "req", !esi.channel<i8> from "resp"]>) channels {req = {name = "loopback_inout.req", type = "cosim"}, resp = {name = "loopback_inout.resp", type = "cosim"}}
// CONN-NEXT: }
// CONN-NEXT: [[r2:%.+]] = esi.cosim.from_host [[clk]], [[rst]], "loopback_inout.req" : !esi.channel<i16>
// CONN-NEXT: %bundle, %resp = esi.bundle.pack [[r2]] : !esi.bundle<[!esi.channel<i16> to "req", !esi.channel<i8> from "resp"]>
Expand Down Expand Up @@ -194,7 +194,7 @@ hw.module @CallableFunc1() {
// CONN-LABEL: hw.module @CallableAccel1(in %clk : !seq.clock, in %rst : i1) {
// CONN-NEXT: hw.instance "func1" @CallableFunc1(func1: %bundle: !esi.bundle<[!esi.channel<i16> to "arg", !esi.channel<i16> from "result"]>) -> ()
// CONN-NEXT: esi.manifest.service_impl #esi.appid<"funcComms"> svc @funcs std "esi.service.std.func" by "cosim" with {} {
// CONN-NEXT: esi.manifest.impl_conn [#esi.appid<"func1">] req <@funcs::@call>(!esi.bundle<[!esi.channel<i16> to "arg", !esi.channel<i16> from "result"]>) with {channel_assignments = {arg = "func1.arg", result = "func1.result"}}
// CONN-NEXT: esi.manifest.impl_conn [#esi.appid<"func1">] req <@funcs::@call>(!esi.bundle<[!esi.channel<i16> to "arg", !esi.channel<i16> from "result"]>) channels {arg = {name = "func1.arg", type = "cosim"}, result = {name = "func1.result", type = "cosim"}}
// CONN-NEXT: }
// CONN-NEXT: %0 = esi.cosim.from_host %clk, %rst, "func1.arg" : !esi.channel<i16>
// CONN-NEXT: %bundle, %result = esi.bundle.pack %0 : !esi.bundle<[!esi.channel<i16> to "arg", !esi.channel<i16> from "result"]>
Expand Down

0 comments on commit 313ac7a

Please sign in to comment.