Skip to content

Commit

Permalink
[ESI][PyCDE] ChannelSignal: add buffer method (#7310)
Browse files Browse the repository at this point in the history
Adds a channel buffer to an ESI channel. Since the channel buffer lowers
to a SystemVerilog primitive, we need to copy the ESI primitives file
into the output dir.
  • Loading branch information
teqdruid authored Jul 12, 2024
1 parent 34263bd commit b68c01d
Show file tree
Hide file tree
Showing 8 changed files with 58 additions and 22 deletions.
7 changes: 5 additions & 2 deletions frontends/PyCDE/integration_test/esi_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -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
Expand Down Expand Up @@ -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)()

Expand Down
14 changes: 14 additions & 0 deletions frontends/PyCDE/src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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 "$<TARGET_PROPERTY:ESIPrimitives,SOURCE_DIR>/$<TARGET_PROPERTY:ESIPrimitives,SOURCES>")
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
Expand Down
1 change: 0 additions & 1 deletion frontends/PyCDE/src/pycde/bsp/xrt.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
15 changes: 1 addition & 14 deletions frontends/PyCDE/src/pycde/esi.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
15 changes: 15 additions & 0 deletions frontends/PyCDE/src/pycde/signals.py
Original file line number Diff line number Diff line change
Expand Up @@ -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."""
Expand Down
4 changes: 4 additions & 0 deletions frontends/PyCDE/src/pycde/system.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
14 changes: 9 additions & 5 deletions frontends/PyCDE/test/test_esi.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -150,18 +150,22 @@ def construct(ports):
print(Bundle1.resp)


# CHECK-LABEL: hw.module @SendBundleTest(in %s1_in : !esi.channel<i32>, out b_send : !esi.bundle<[!esi.channel<i32> to "req", !esi.channel<i1> from "resp"]>, out i1_out : !esi.channel<i1>) attributes {output_file = #hw.output_file<"SendBundleTest.sv", includeReplicatedOps>} {
# CHECK-NEXT: %bundle, %resp = esi.bundle.pack %s1_in : !esi.bundle<[!esi.channel<i32> to "req", !esi.channel<i1> from "resp"]>
# CHECK-LABEL: hw.module @SendBundleTest(in %clk : !seq.clock, in %rst : i1, in %s1_in : !esi.channel<i32>, out b_send : !esi.bundle<[!esi.channel<i32> to "req", !esi.channel<i1> from "resp"]>, out i1_out : !esi.channel<i1>) 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<i32> to "req", !esi.channel<i1> from "resp"]>
# CHECK-NEXT: hw.output %bundle, %resp : !esi.bundle<[!esi.channel<i32> to "req", !esi.channel<i1> from "resp"]>, !esi.channel<i1>
@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


Expand Down
10 changes: 10 additions & 0 deletions lib/Dialect/ESI/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down

0 comments on commit b68c01d

Please sign in to comment.