diff --git a/frontends/PyCDE/src/esi.py b/frontends/PyCDE/src/esi.py index 5a4a213caf..05670bf5ae 100644 --- a/frontends/PyCDE/src/esi.py +++ b/frontends/PyCDE/src/esi.py @@ -4,10 +4,10 @@ from .common import (AppID, Input, Output, _PyProxy, PortError) from .module import Generator, Module, ModuleLikeBuilderBase, PortProxyBase -from .signals import BundleSignal, ChannelSignal, Signal, _FromCirctValue +from .signals import BundleSignal, ChannelSignal, Signal, Struct, _FromCirctValue from .system import System from .types import (Bits, Bundle, BundledChannel, Channel, ChannelDirection, - Type, types, _FromCirctType) + StructType, Type, types, UInt, _FromCirctType) from .circt import ir from .circt.dialects import esi as raw_esi, hw, msft @@ -473,6 +473,21 @@ class PureModule(Module): esi.ESIPureModuleParamOp(name, type_attr) +@ServiceDecl +class MMIO: + """ESI standard service to request access to an MMIO region.""" + + read = ServiceDecl.From( + Bundle([ + BundledChannel("offset", ChannelDirection.TO, Bits(32)), + BundledChannel("data", ChannelDirection.FROM, Bits(32)) + ])) + + @staticmethod + def _op(sym_name: ir.StringAttr): + return raw_esi.MMIOServiceDeclOp(sym_name) + + def package(sys: System): """Package all ESI collateral.""" diff --git a/frontends/PyCDE/test/test_esi.py b/frontends/PyCDE/test/test_esi.py index 5a7319655c..e153bd3ab7 100644 --- a/frontends/PyCDE/test/test_esi.py +++ b/frontends/PyCDE/test/test_esi.py @@ -1,11 +1,12 @@ # RUN: rm -rf %t # RUN: %PYTHON% %s %t 2>&1 | FileCheck %s -from pycde import (Clock, Input, InputChannel, OutputChannel, Module, generator, - types) +from pycde import (Clock, Input, InputChannel, OutputChannel, Module, System, + generator, types) from pycde import esi from pycde.common import AppID, Output, RecvBundle, SendBundle from pycde.constructs import Wire +from pycde.esi import MMIO from pycde.types import (Bits, Bundle, BundledChannel, Channel, ChannelDirection, ChannelSignaling, UInt, ClockType) from pycde.testing import unittestmodule @@ -126,3 +127,23 @@ class RecvBundleTest(Module): def build(self): to_channels = self.b_recv.unpack(resp=self.i1_in) self.s1_out = to_channels['req'] + + +# CHECK-LABEL: hw.module @MMIOReq() +# CHECK-NEXT: %c0_i32 = hw.constant 0 : i32 +# CHECK-NEXT: %false = hw.constant false +# CHECK-NEXT: [[B:%.+]] = esi.service.req.to_client <@MMIO::@read>(#esi.appid<"mmio_req">) : !esi.bundle<[!esi.channel to "offset", !esi.channel from "data"]> +# CHECK-NEXT: %chanOutput, %ready = esi.wrap.vr %c0_i32, %false : i32 +# CHECK-NEXT: %offset = esi.bundle.unpack %chanOutput from [[B]] : !esi.bundle<[!esi.channel to "offset", !esi.channel from "data"]> +@unittestmodule(esi_sys=True) +class MMIOReq(Module): + + @generator + def build(ports): + c32 = Bits(32)(0) + c1 = Bits(1)(0) + + read_bundle = MMIO.read(AppID("mmio_req")) + + data, _ = Channel(Bits(32)).wrap(c32, c1) + _ = read_bundle.unpack(data=data) diff --git a/lib/Dialect/ESI/ESIStdServices.cpp b/lib/Dialect/ESI/ESIStdServices.cpp index 7c2f6c6d30..828da8fc90 100644 --- a/lib/Dialect/ESI/ESIStdServices.cpp +++ b/lib/Dialect/ESI/ESIStdServices.cpp @@ -89,10 +89,9 @@ void MMIOServiceDeclOp::getPortList(SmallVectorImpl &ports) { ServicePortInfo::Direction::toClient, ChannelBundleType::get( ctxt, - {BundledChannel{StringAttr::get(ctxt, "offset"), - ChannelDirection::from, + {BundledChannel{StringAttr::get(ctxt, "offset"), ChannelDirection::to, ChannelType::get(ctxt, IntegerType::get(ctxt, 32))}, - BundledChannel{StringAttr::get(ctxt, "data"), ChannelDirection::to, + BundledChannel{StringAttr::get(ctxt, "data"), ChannelDirection::from, ChannelType::get(ctxt, IntegerType::get(ctxt, 32))}}, /*resettable=*/UnitAttr())}); // Write only port. @@ -101,10 +100,9 @@ void MMIOServiceDeclOp::getPortList(SmallVectorImpl &ports) { ServicePortInfo::Direction::toClient, ChannelBundleType::get( ctxt, - {BundledChannel{StringAttr::get(ctxt, "offset"), - ChannelDirection::from, + {BundledChannel{StringAttr::get(ctxt, "offset"), ChannelDirection::to, ChannelType::get(ctxt, IntegerType::get(ctxt, 32))}, - BundledChannel{StringAttr::get(ctxt, "data"), ChannelDirection::from, + BundledChannel{StringAttr::get(ctxt, "data"), ChannelDirection::to, ChannelType::get(ctxt, IntegerType::get(ctxt, 32))}}, /*resettable=*/UnitAttr())}); } diff --git a/test/Dialect/ESI/services.mlir b/test/Dialect/ESI/services.mlir index a3404d6267..697f69b5aa 100644 --- a/test/Dialect/ESI/services.mlir +++ b/test/Dialect/ESI/services.mlir @@ -205,11 +205,11 @@ hw.module @CallableAccel1(in %clk: !seq.clock, in %rst: i1) { } esi.service.std.mmio @mmio -!mmioReq = !esi.bundle<[!esi.channel from "offset", !esi.channel to "data"]> +!mmioReq = !esi.bundle<[!esi.channel to "offset", !esi.channel from "data"]> -// CONN-LABEL: hw.module @MMIOManifest(in %clk : !seq.clock, in %rst : i1, in %manifest : !esi.bundle<[!esi.channel from "offset", !esi.channel to "data"]>) { -// CONN-NEXT: esi.manifest.req #esi.appid<"manifest">, <@mmio::@read> std "esi.service.std.mmio", toClient, !esi.bundle<[!esi.channel from "offset", !esi.channel to "data"]> -// CONN-NEXT: %data = esi.bundle.unpack %data from %manifest : !esi.bundle<[!esi.channel from "offset", !esi.channel to "data"]> +// CONN-LABEL: hw.module @MMIOManifest(in %clk : !seq.clock, in %rst : i1, in %manifest : !esi.bundle<[!esi.channel to "offset", !esi.channel from "data"]>) { +// CONN-NEXT: esi.manifest.req #esi.appid<"manifest">, <@mmio::@read> std "esi.service.std.mmio", toClient, !esi.bundle<[!esi.channel to "offset", !esi.channel from "data"]> +// CONN-NEXT: %offset = esi.bundle.unpack %offset from %manifest : !esi.bundle<[!esi.channel to "offset", !esi.channel from "data"]> hw.module @MMIOManifest(in %clk: !seq.clock, in %rst: i1) { %req = esi.service.req.to_client <@mmio::@read> (#esi.appid<"manifest">) : !mmioReq %loopback = esi.bundle.unpack %loopback from %req : !mmioReq