From d8789fc72dc2b798866a406fecf37e1282c6492a Mon Sep 17 00:00:00 2001 From: Srikanth Vuppala <120363307+svup-xilinx@users.noreply.github.com> Date: Wed, 5 Aug 2026 11:49:13 +0530 Subject: [PATCH] Add Python bindings for xrt::graph and xrt::aie::bo. Expose AIE graph execution and GMIO buffer APIs in pyxrt, and mark graph/AIE symbols with XRT_API_EXPORT so pyxrt links on Windows. Co-authored-by: Cursor Signed-off-by: Srikanth Vuppala <120363307+svup-xilinx@users.noreply.github.com> --- src/python/pybind11/pyxrt.pyi | 195 +++++++++++++++++++ src/python/pybind11/src/pyxrt.cpp | 117 +++++++++++ src/runtime_src/core/include/xrt/xrt_aie.h | 11 ++ src/runtime_src/core/include/xrt/xrt_graph.h | 13 ++ 4 files changed, 336 insertions(+) diff --git a/src/python/pybind11/pyxrt.pyi b/src/python/pybind11/pyxrt.pyi index d1523f1c34d..9f0ffcb55a7 100644 --- a/src/python/pybind11/pyxrt.pyi +++ b/src/python/pybind11/pyxrt.pyi @@ -1186,3 +1186,198 @@ class ext: shared: _ext_access_mode process: _ext_access_mode hybrid: _ext_access_mode + + +class graph: + """Represents an AIE graph execution object.""" + + class access_mode(IntEnum): + """Graph access mode.""" + exclusive: graph.access_mode + primary: graph.access_mode + shared: graph.access_mode + + # Class-level enum value aliases + exclusive: access_mode + primary: access_mode + shared: access_mode + + @overload + def __init__(self, ctx: hw_context, name: str) -> None: + """Create a graph from a hardware context and graph name. + + Args: + ctx: The hardware context. + name: Name of the graph. + """ + ... + + @overload + def __init__(self, ctx: hw_context, name: str, mode: access_mode) -> None: + """Create a graph with an explicit access mode. + + Args: + ctx: The hardware context. + name: Name of the graph. + mode: Access mode (exclusive, primary, or shared). + """ + ... + + def reset(self) -> None: + """Reset the graph by disabling tiles and enabling tile reset.""" + ... + + def get_timestamp(self) -> int: + """Get the current graph timestamp in AIE cycles. + + Returns: + Timestamp in AIE cycles. + """ + ... + + def gmio_bank_id(self, gmio_name: str) -> int: + """Get the memory bank index for a named GMIO port. + + Args: + gmio_name: GMIO port name. + + Returns: + Memory bank group ID. + """ + ... + + def run(self, iterations: int = 0) -> None: + """Start graph execution. + + Args: + iterations: Number of iterations (0 = run forever). + """ + ... + + @overload + def wait(self, timeout_ms: SupportsInt) -> None: + """Wait for graph completion or until timeout. + + Args: + timeout_ms: Timeout in milliseconds. + """ + ... + + @overload + def wait(self, cycles: SupportsInt = 0) -> None: + """Wait for the given AIE cycles then suspend the graph. + + Args: + cycles: AIE cycles to wait (0 = block until done). + """ + ... + + def suspend(self) -> None: + """Suspend a running graph.""" + ... + + def resume(self) -> None: + """Resume a suspended graph.""" + ... + + def end(self, cycles: SupportsInt = 0) -> None: + """Wait for the given AIE cycles then terminate the graph. + + Args: + cycles: AIE cycles to wait (0 = block until done). + """ + ... + + def update(self, port_name: str, data: ReadableBuffer) -> None: + """Update a runtime parameter port. + + Args: + port_name: Hierarchical RTP port name. + data: Data to write (any buffer protocol object: bytes, bytearray, memoryview, numpy array, etc.). + """ + ... + + def read(self, port_name: str, size: SupportsInt) -> NDArrayInt8: + """Read a runtime parameter port. + + Args: + port_name: Hierarchical RTP port name. + size: Number of bytes to read. + + Returns: + NumPy array containing the read data. + """ + ... + + +class _aie_bo(bo): + """AIE buffer object supporting GMIO synchronous transfers.""" + + @overload + def __init__( + self, device: device, size: SupportsInt, flags: bo.flags, group: SupportsInt + ) -> None: + """Create a buffer object with specified properties. + + Args: + device: The device to allocate the buffer on. + size: Size of the buffer in bytes. + flags: Buffer creation flags. + group: Memory bank group ID. + """ + ... + + @overload + def __init__( + self, ctx: hw_context, size: SupportsInt, flags: bo.flags, group: SupportsInt + ) -> None: + """Create a buffer object with specified properties in a hardware context. + + Args: + ctx: The hardware context to allocate the buffer in. + size: Size of the buffer in bytes. + flags: Buffer creation flags. + group: Memory bank group ID. + """ + ... + + @overload + def __init__(self, ctx: hw_context, size: SupportsInt, group: SupportsInt) -> None: + """Create a buffer object with default flags in a hardware context. + + Args: + ctx: The hardware context to allocate the buffer in. + size: Size of the buffer in bytes. + group: Memory bank group ID. + """ + ... + + @overload + def sync( + self, port: str, direction: xclBOSyncDirection, size: SupportsInt, offset: SupportsInt + ) -> None: + """Synchronize buffer data with a named GMIO port. + + Args: + port: GMIO port name. + direction: Direction of synchronization. + size: Number of bytes to sync. + offset: Offset in the buffer. + """ + ... + + @overload + def sync(self, port: str, direction: xclBOSyncDirection) -> None: + """Sync entire buffer content with a named GMIO port. + + Args: + port: GMIO port name. + direction: Direction of synchronization. + """ + ... + + +class aie: + """AIE-specific XRT functionality.""" + + bo = _aie_bo diff --git a/src/python/pybind11/src/pyxrt.cpp b/src/python/pybind11/src/pyxrt.cpp index 50bb6c5b3ef..a7e4f8d4b21 100644 --- a/src/python/pybind11/src/pyxrt.cpp +++ b/src/python/pybind11/src/pyxrt.cpp @@ -15,6 +15,7 @@ #include "xrt/xrt_device.h" #include "xrt/xrt_kernel.h" #include "xrt/xrt_bo.h" +#include "xrt/xrt_graph.h" #include "xrt/experimental/xrt_module.h" #include "xrt/experimental/xrt_message.h" #include "xrt/experimental/xrt_system.h" @@ -746,4 +747,120 @@ PYBIND11_MODULE(pyxrt, m) { }), py::arg("timeout"), "Wait for the specified timeout for the runlist to complete"); +/* + * + * xrt::graph + * + */ + + py::class_ pygraph(m, "graph", "Represents an AIE graph execution object"); + + py::enum_(pygraph, "access_mode", "Graph access mode") + .value("exclusive", xrt::graph::access_mode::exclusive) + .value("primary", xrt::graph::access_mode::primary) + .value("shared", xrt::graph::access_mode::shared) + .export_values(); + + pygraph + .def(py::init([](const xrt::hw_context& ctx, const std::string& name) { + py::gil_scoped_release release; + return new xrt::graph(ctx, name); + }), + py::arg("ctx"), py::arg("name"), + "Create a graph from a hardware context and graph name.") + .def(py::init([](const xrt::hw_context& ctx, const std::string& name, + xrt::graph::access_mode mode) { + py::gil_scoped_release release; + return new xrt::graph(ctx, name, mode); + }), + py::arg("ctx"), py::arg("name"), py::arg("mode"), + "Create a graph with an explicit access mode.") + .def("reset", [](xrt::graph& g) { + py::gil_scoped_release release; + g.reset(); + }, "Reset the graph by disabling tiles and enabling tile reset.") + .def("get_timestamp", &xrt::graph::get_timestamp, + "Get the current graph timestamp in AIE cycles.") + .def("gmio_bank_id", &xrt::graph::gmio_bank_id, py::arg("gmio_name"), + "Get the memory bank index for a named GMIO port.") + .def("run", [](xrt::graph& g, uint32_t iterations) { + py::gil_scoped_release release; + g.run(iterations); + }, py::arg("iterations") = 0, + "Start graph execution.") + .def("wait", [](xrt::graph& g, std::chrono::milliseconds timeout_ms) { + py::gil_scoped_release release; + g.wait(timeout_ms); + }, py::arg("timeout_ms"), + "Wait for graph completion or until timeout.") + .def("wait", [](xrt::graph& g, uint64_t cycles) { + py::gil_scoped_release release; + g.wait(cycles); + }, py::arg("cycles") = 0, + "Wait for the given AIE cycles then suspend the graph.") + .def("suspend", [](xrt::graph& g) { + py::gil_scoped_release release; + g.suspend(); + }, "Suspend a running graph.") + .def("resume", [](xrt::graph& g) { + py::gil_scoped_release release; + g.resume(); + }, "Resume a suspended graph.") + .def("end", [](xrt::graph& g, uint64_t cycles) { + py::gil_scoped_release release; + g.end(cycles); + }, py::arg("cycles") = 0, + "Wait for the given AIE cycles then terminate the graph.") + .def("update", [](xrt::graph& g, const std::string& port_name, py::buffer data) { + py::buffer_info info = data.request(); + g.update(port_name, info.ptr, static_cast(info.itemsize * info.size)); + }, py::arg("port_name"), py::arg("data"), + "Update a runtime parameter port.") + .def("read", [](xrt::graph& g, const std::string& port_name, size_t size) { + py::array_t result(size); + py::buffer_info info = result.request(); + g.read(port_name, info.ptr, size); + return result; + }, py::arg("port_name"), py::arg("size"), + "Read a runtime parameter port."); + +/* + * + * xrt::aie::bo + * + */ + + py::module_ aie = m.def_submodule("aie", "AIE-specific XRT functionality."); + + py::class_ pyaiebo(aie, "bo", + "AIE buffer object supporting GMIO synchronous transfers."); + + pyaiebo + .def(py::init([](xrt::device& d, size_t sz, xrt::bo::flags flags, xrt::memory_group grp) { + return new xrt::aie::bo(d, sz, flags, grp); + }), + py::arg("device"), py::arg("size"), py::arg("flags"), py::arg("group"), + "Create a buffer object on a device with the requested size, flags, and memory group.") + .def(py::init([](xrt::hw_context& ctx, size_t sz, xrt::bo::flags flags, xrt::memory_group grp) { + return new xrt::aie::bo(ctx, sz, flags, grp); + }), + py::arg("ctx"), py::arg("size"), py::arg("flags"), py::arg("group"), + "Create a buffer object in a hardware context with the requested size, flags, and memory group.") + .def(py::init([](xrt::hw_context& ctx, size_t sz, xrt::memory_group grp) { + return new xrt::aie::bo(ctx, sz, grp); + }), + py::arg("ctx"), py::arg("size"), py::arg("group"), + "Create a buffer object in a hardware context using default flags.") + .def("sync", [](xrt::aie::bo& b, const std::string& port, + xclBOSyncDirection dir, size_t sz, size_t offset) { + py::gil_scoped_release release; + b.sync(port, dir, sz, offset); + }, py::arg("port"), py::arg("direction"), py::arg("size"), py::arg("offset"), + "Synchronize buffer data with a named GMIO port.") + .def("sync", [](xrt::aie::bo& b, const std::string& port, xclBOSyncDirection dir) { + py::gil_scoped_release release; + b.sync(port, dir); + }, py::arg("port"), py::arg("direction"), + "Sync entire buffer content with a named GMIO port."); + } diff --git a/src/runtime_src/core/include/xrt/xrt_aie.h b/src/runtime_src/core/include/xrt/xrt_aie.h index fc6bf9dc0fa..95c2719f416 100644 --- a/src/runtime_src/core/include/xrt/xrt_aie.h +++ b/src/runtime_src/core/include/xrt/xrt_aie.h @@ -94,6 +94,7 @@ class device : public xrt::device * Reset shim; * Write '0' to all the data and program memories. */ + XRT_API_EXPORT void reset_array(); @@ -260,6 +261,7 @@ class bo : public xrt::bo * Asynchronously transfer the buffer contents from BO offset to offset + sz * between GMIO and AIE. */ + XRT_API_EXPORT async_handle async(const std::string& port, xclBOSyncDirection dir, size_t sz, size_t offset); @@ -280,6 +282,7 @@ class bo : public xrt::bo * * The current thread will block until the transfer is completed. */ + XRT_API_EXPORT void sync(const std::string& port, xclBOSyncDirection dir, size_t sz, size_t offset); /** @@ -319,6 +322,7 @@ class hw_context : public xrt::hw_context * reset_array() - reset the AIE Array used for this hw_context * */ + XRT_API_EXPORT void reset_array(); /** @@ -329,6 +333,7 @@ class hw_context : public xrt::hw_context * This function retrieves the current AIE frequency for the partition * associated with this hardware context. */ + XRT_API_EXPORT double get_aie_freq() const; @@ -340,6 +345,7 @@ class hw_context : public xrt::hw_context * This function sets the AIE frequency for the partition * associated with this hardware context. */ + XRT_API_EXPORT void set_aie_freq(double /* freq_mhz*/); @@ -386,9 +392,11 @@ class profiling : public detail::pimpl * */ [[deprecated("deprecated, please use profiling(hw_context) instead")]] + XRT_API_EXPORT explicit profiling(const xrt::device& device); + XRT_API_EXPORT explicit profiling(const xrt::hw_context& hwctx); @@ -410,6 +418,7 @@ class profiling : public detail::pimpl * port names and value. The port names and value will have different * meanings on different options. */ + XRT_API_EXPORT int start(profiling_option option, const std::string& port1_name, const std::string& port2_name, uint32_t value) const; @@ -417,6 +426,7 @@ class profiling : public detail::pimpl * read() - Read the current performance counter value * associated with the profiling handle */ + XRT_API_EXPORT uint64_t read() const; @@ -425,6 +435,7 @@ class profiling : public detail::pimpl * associated with the profiling handle and * release the corresponding hardware resources. */ + XRT_API_EXPORT void stop() const; }; diff --git a/src/runtime_src/core/include/xrt/xrt_graph.h b/src/runtime_src/core/include/xrt/xrt_graph.h index d56a9d53f4c..bee179a8d1c 100644 --- a/src/runtime_src/core/include/xrt/xrt_graph.h +++ b/src/runtime_src/core/include/xrt/xrt_graph.h @@ -65,6 +65,7 @@ class graph * Open the graph with specified access (default primary) */ [[deprecated("deprecated, please use graph(hw_context, name) instead")]] + XRT_API_EXPORT graph(const xrt::device& device, const xrt::uuid& xclbin_id, const std::string& name, access_mode am = access_mode::primary); @@ -78,6 +79,7 @@ class graph * @param am * Open the graph with specified access (default primary) */ + XRT_API_EXPORT graph(const xrt::hw_context& ctx, const std::string& name, access_mode am = access_mode::primary); @@ -86,6 +88,7 @@ class graph * * Reset graph by disabling tiles and enable tiles reset */ + XRT_API_EXPORT void reset() const; @@ -95,6 +98,7 @@ class graph * @return * Timestamp in AIE cycle */ + XRT_API_EXPORT uint64_t get_timestamp() const; @@ -106,6 +110,7 @@ class graph * @return * Bank index for xrt::aie::bo(hwctx, size, flags, bank_id). */ + XRT_API_EXPORT uint32_t gmio_bank_id(const std::string& gmio_name) const; @@ -121,6 +126,7 @@ class graph * default, run forever; or run a fixed number of iterations if specified * during compilation time. */ + XRT_API_EXPORT void run(uint32_t iterations = 0); @@ -135,6 +141,7 @@ class graph * * The current thread will block until graph run completes or timeout. */ + XRT_API_EXPORT void wait(std::chrono::milliseconds timeout_ms); @@ -155,6 +162,7 @@ class graph * * The current thread will block until graph is paused. */ + XRT_API_EXPORT void wait(uint64_t cycles = 0); @@ -163,6 +171,7 @@ class graph * * Suspend graph execution. */ + XRT_API_EXPORT void suspend(); @@ -171,6 +180,7 @@ class graph * * Resume graph execution which was paused by suspend() or wait(cycles) APIs */ + XRT_API_EXPORT void resume(); @@ -191,6 +201,7 @@ class graph * * The current thread will block until graph is terminated. */ + XRT_API_EXPORT void end(uint64_t cycles = 0); @@ -259,9 +270,11 @@ class graph private: std::shared_ptr handle; + XRT_API_EXPORT void update_port(const std::string& port_name, const void* value, size_t bytes); + XRT_API_EXPORT void read_port(const std::string& port_name, void* value, size_t bytes); };