diff --git a/frontends/PyCDE/integration_test/esi_test.py b/frontends/PyCDE/integration_test/esi_test.py index 401574525e29..48966add3e68 100644 --- a/frontends/PyCDE/integration_test/esi_test.py +++ b/frontends/PyCDE/integration_test/esi_test.py @@ -16,6 +16,8 @@ class LoopbackInOutAdd7(Module): """Loopback the request from the host, adding 7 to the first 15 bits.""" + clk = Clock() + rst = Reset() @generator def construct(ports): @@ -28,8 +30,9 @@ def construct(ports): data, valid = args.unwrap(ready) plus7 = data + 7 data_chan, data_ready = loopback.type.wrap(plus7.as_uint(16), valid) + data_chan_buffered = data_chan.buffer(ports.clk, ports.rst, 5) ready.assign(data_ready) - loopback.assign(data_chan) + loopback.assign(data_chan_buffered) @modparams @@ -61,7 +64,7 @@ class Top(Module): @generator def construct(ports): - LoopbackInOutAdd7() + LoopbackInOutAdd7(clk=ports.clk, rst=ports.rst) for i in range(4, 18, 5): MMIOClient(i)() diff --git a/frontends/PyCDE/src/CMakeLists.txt b/frontends/PyCDE/src/CMakeLists.txt index 513d17578036..48d811ebd24d 100644 --- a/frontends/PyCDE/src/CMakeLists.txt +++ b/frontends/PyCDE/src/CMakeLists.txt @@ -88,6 +88,20 @@ add_mlir_python_modules(PyCDE add_dependencies(PyCDE PyCDE_CIRCTPythonModules) add_dependencies(install-PyCDE install-PyCDE_CIRCTPythonModules) +# Copy ESIPrimitives.sv to both the build and install directories. +# TODO: this won't work if ESIPrimitives has multiple source files. Figure out +# how to handle this. +set(esiprims "$/$") +add_custom_command(TARGET PyCDE POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + ${esiprims} + ${PYCDE_PYTHON_PACKAGE_DIR}/pycde +) +install(FILES ${esiprims} + DESTINATION python_packages/pycde + COMPONENT PyCDE +) + install(IMPORTED_RUNTIME_ARTIFACTS PyCDE_CIRCTPythonCAPI RUNTIME_DEPENDENCY_SET PyCDE_RUNTIME_DEPS DESTINATION python_packages/pycde/circt/_mlir_libs diff --git a/frontends/PyCDE/src/pycde/bsp/xrt.py b/frontends/PyCDE/src/pycde/bsp/xrt.py index ea47f814d5e7..1a2a7036199b 100644 --- a/frontends/PyCDE/src/pycde/bsp/xrt.py +++ b/frontends/PyCDE/src/pycde/bsp/xrt.py @@ -171,7 +171,6 @@ def construct(ports): # Copy additional sources sys: System = System.current() - sys.add_packaging_step(esi.package) sys.add_packaging_step(XrtTop.package) @staticmethod diff --git a/frontends/PyCDE/src/pycde/esi.py b/frontends/PyCDE/src/pycde/esi.py index 31e46ff5c316..f6f3873f9dc7 100644 --- a/frontends/PyCDE/src/pycde/esi.py +++ b/frontends/PyCDE/src/pycde/esi.py @@ -613,20 +613,7 @@ def package(sys: System): """Package all ESI collateral.""" import shutil - __root_dir__ = Path(__file__).parent - - # When pycde is installed through a proper install, all of the collateral - # files are under a dir called "collateral". - collateral_dir = __root_dir__ / "collateral" - if collateral_dir.exists(): - esi_lib_dir = collateral_dir - else: - # Build we also want to allow pycde to work in-tree for developers. The - # necessary files are screwn around the build tree. - build_dir = __root_dir__.parents[4] - circt_lib_dir = build_dir / "tools" / "circt" / "lib" - esi_lib_dir = circt_lib_dir / "Dialect" / "ESI" - # shutil.copy(esi_lib_dir / "ESIPrimitives.sv", sys.hw_output_dir) + shutil.copy(__dir__ / "ESIPrimitives.sv", sys.hw_output_dir) def ChannelDemux2(data_type: Type): diff --git a/frontends/PyCDE/src/pycde/signals.py b/frontends/PyCDE/src/pycde/signals.py index daac86901603..044e9c7a4a13 100644 --- a/frontends/PyCDE/src/pycde/signals.py +++ b/frontends/PyCDE/src/pycde/signals.py @@ -721,6 +721,21 @@ def unwrap(self, readyOrRden): else: raise TypeError("Unknown signaling standard") + def buffer(self, clk: ClockSignal, reset: BitsSignal, + stages: int) -> ChannelSignal: + """Insert a channel buffer with `stages` stages on the channel. Return the + output of that buffer.""" + + from .dialects import esi + return ChannelSignal( + esi.ChannelBufferOp( + self.type, + clk, + reset, + self.value, + stages=stages, + ), self.type) + class BundleSignal(Signal): """Signal for types.Bundle.""" diff --git a/frontends/PyCDE/src/pycde/system.py b/frontends/PyCDE/src/pycde/system.py index 193d1faf42bd..c893c4e7fc7f 100644 --- a/frontends/PyCDE/src/pycde/system.py +++ b/frontends/PyCDE/src/pycde/system.py @@ -347,6 +347,10 @@ def package(self): assert self.passed, "Must call compile before package" for func in self.packaging_funcs: func(self) + # Since PyCDE is currently being used pretty much exclusively for ESI, it's + # fair to just package the ESI collateral always. + from .esi import package as esi_package + esi_package(self) class _OpCache: diff --git a/frontends/PyCDE/test/test_esi.py b/frontends/PyCDE/test/test_esi.py index cc9bbacda61c..92f5ca67930f 100644 --- a/frontends/PyCDE/test/test_esi.py +++ b/frontends/PyCDE/test/test_esi.py @@ -1,8 +1,8 @@ # 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, Reset, + generator, types) from pycde import esi from pycde.common import AppID, RecvBundle, SendBundle from pycde.constructs import Wire @@ -150,18 +150,22 @@ def construct(ports): print(Bundle1.resp) -# CHECK-LABEL: hw.module @SendBundleTest(in %s1_in : !esi.channel, out b_send : !esi.bundle<[!esi.channel to "req", !esi.channel from "resp"]>, out i1_out : !esi.channel) attributes {output_file = #hw.output_file<"SendBundleTest.sv", includeReplicatedOps>} { -# CHECK-NEXT: %bundle, %resp = esi.bundle.pack %s1_in : !esi.bundle<[!esi.channel to "req", !esi.channel from "resp"]> +# CHECK-LABEL: hw.module @SendBundleTest(in %clk : !seq.clock, in %rst : i1, in %s1_in : !esi.channel, out b_send : !esi.bundle<[!esi.channel to "req", !esi.channel from "resp"]>, out i1_out : !esi.channel) attributes {output_file = #hw.output_file<"SendBundleTest.sv", includeReplicatedOps>} { +# CHECK-NEXT: [[B0:%.+]] = esi.buffer %clk, %rst, %s1_in {stages = 4 : i64} : i32 +# CHECK-NEXT: %bundle, %resp = esi.bundle.pack [[B0]] : !esi.bundle<[!esi.channel to "req", !esi.channel from "resp"]> # CHECK-NEXT: hw.output %bundle, %resp : !esi.bundle<[!esi.channel to "req", !esi.channel from "resp"]>, !esi.channel @unittestmodule() class SendBundleTest(Module): + clk = Clock() + rst = Reset() b_send = SendBundle(Bundle1) s1_in = InputChannel(types.i32) i1_out = OutputChannel(types.i1) @generator def build(self): - self.b_send, from_chans = Bundle1.pack(req=self.s1_in) + s1_buffered = self.s1_in.buffer(self.clk, self.rst, 4) + self.b_send, from_chans = Bundle1.pack(req=s1_buffered) self.i1_out = from_chans.resp diff --git a/lib/Dialect/ESI/CMakeLists.txt b/lib/Dialect/ESI/CMakeLists.txt index df167290ce5d..abe8633ffc50 100644 --- a/lib/Dialect/ESI/CMakeLists.txt +++ b/lib/Dialect/ESI/CMakeLists.txt @@ -62,6 +62,16 @@ add_circt_dialect_library(CIRCTESI ${ESI_LinkLibs} ) +add_custom_target(ESIPrimitives +SOURCES + ESIPrimitives.sv +) +add_custom_command(TARGET ESIPrimitives POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + ${CMAKE_CURRENT_SOURCE_DIR}/ESIPrimitives.sv + ${CMAKE_CURRENT_BINARY_DIR}/ESIPrimitives.sv +) + option(ESI_RUNTIME "Build and test the ESI runtime" OFF) llvm_canonicalize_cmake_booleans(ESI_RUNTIME) if (ESI_RUNTIME)