From 0cd857f5812292506fac8c48050dd36ad11984ed Mon Sep 17 00:00:00 2001 From: John Demme Date: Sat, 20 Jan 2024 03:05:53 +0000 Subject: [PATCH] [PyCDE] Remove a bunch of old cruft A lot of this functionality was replaced by the ESI runtime. --- .../esi_ram_cpp/CMakeLists.txt | 102 ------ .../integration_test/esi_ram_cpp/esi_ram.cpp | 138 -------- .../esi_ram_cpp/lit.local.cfg | 1 - frontends/PyCDE/src/CMakeLists.txt | 39 --- frontends/PyCDE/src/Makefile.cosim | 23 -- frontends/PyCDE/src/bsp/EsiXrtPython.cpp | 58 ---- frontends/PyCDE/src/esi_api.py | 157 --------- frontends/PyCDE/src/esi_api.py.j2 | 147 -------- frontends/PyCDE/src/esi_runtime_common.py | 324 ------------------ frontends/PyCDE/src/system.py | 1 - 10 files changed, 990 deletions(-) delete mode 100644 frontends/PyCDE/integration_test/esi_ram_cpp/CMakeLists.txt delete mode 100644 frontends/PyCDE/integration_test/esi_ram_cpp/esi_ram.cpp delete mode 100644 frontends/PyCDE/integration_test/esi_ram_cpp/lit.local.cfg delete mode 100644 frontends/PyCDE/src/Makefile.cosim delete mode 100644 frontends/PyCDE/src/bsp/EsiXrtPython.cpp delete mode 100644 frontends/PyCDE/src/esi_api.py delete mode 100644 frontends/PyCDE/src/esi_api.py.j2 delete mode 100644 frontends/PyCDE/src/esi_runtime_common.py diff --git a/frontends/PyCDE/integration_test/esi_ram_cpp/CMakeLists.txt b/frontends/PyCDE/integration_test/esi_ram_cpp/CMakeLists.txt deleted file mode 100644 index 1ff10000bc..0000000000 --- a/frontends/PyCDE/integration_test/esi_ram_cpp/CMakeLists.txt +++ /dev/null @@ -1,102 +0,0 @@ -cmake_minimum_required(VERSION 3.13.4) -project(esi_ram_test) - -set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_STANDARD_REQUIRED ON) - -# TODO: most of this stuff should be moved to a .cmake file that is included. - -# fetch https://github.com/veselink1/refl-cpp -include(FetchContent) -FetchContent_Declare( - refl-cpp - GIT_REPOSITORY https://github.com/veselink1/refl-cpp - GIT_TAG v0.12.4 -) -FetchContent_MakeAvailable(refl-cpp) - -# Assert that CIRCT_DIR is defined. -if(NOT DEFINED CIRCT_DIR) - message(FATAL_ERROR "CIRCT_DIR must be defined.") -endif() - -if(NOT DEFINED PYCDE_OUT_DIR) - message(FATAL_ERROR "PYCDE_OUT_DIR must be defined.") -endif() - -message(STATUS "CIRCT_DIR= ${CIRCT_DIR}") -message(STATUS "PYCDE_OUT_DIR= ${PYCDE_OUT_DIR}") - -set(CAPNP_SCHEMA "${PYCDE_OUT_DIR}/hw/schema.capnp") -set(ESI_CPP_API "${PYCDE_OUT_DIR}/hw/ESISystem.h") -set(ESI_HW_INCLUDE_DIR "${PYCDE_OUT_DIR}/hw") - -# Ensure that the above files are present -if(NOT EXISTS ${CAPNP_SCHEMA}) - message(FATAL_ERROR "CAPNP_SCHEMA not found: ${CAPNP_SCHEMA}") -endif() - -if(NOT EXISTS ${ESI_CPP_API}) - message(FATAL_ERROR "ESI_CPP_API not found: ${ESI_CPP_API}") -endif() - - -if(DEFINED CAPNP_PATH) - set(ENV{PKG_CONFIG_PATH} - "${CAPNP_PATH}/lib/pkgconfig:$ENV{PKG_CONFIG_PATH}") - find_package(CapnProto CONFIG PATHS ${CAPNP_PATH}) - else() - set(ENV{PKG_CONFIG_PATH} - "${CIRCT_DIR}/ext/lib/pkgconfig:$ENV{PKG_CONFIG_PATH}") - find_package(CapnProto CONFIG PATHS "${CIRCT_DIR}/ext") -endif() - -if (NOT CapnProto_FOUND) - message(FATAL_ERROR "Cap'n Proto not found.") -endif() - -# Move schema to the build directory - required by capnp_generate_cpp. -set(CAPNPC_SRC_PREFIX ${CMAKE_CURRENT_BINARY_DIR}/capnp_generated) -set(CAPNPC_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/capnp_generated) -file(COPY ${CAPNP_SCHEMA} DESTINATION ${CAPNPC_OUTPUT_DIR}) -get_filename_component(CAPNP_SCHEMA_BASENAME ${CAPNP_SCHEMA} NAME) -set(COPIED_CAPNP_SCHEMA ${CAPNP_OUTDIR}/${CAPNP_SCHEMA_BASENAME}) -capnp_generate_cpp( - ESI_RAM_SRCS - ESI_RAM_HDRS - ${CAPNPC_SRC_PREFIX}/${CAPNP_SCHEMA_BASENAME} -) - -message(STATUS "CAPNP_OUTDIR= ${CAPNP_OUTDIR}") -add_executable(esi_ram_test - esi_ram.cpp - ${ESI_RAM_SRCS} - ${ESI_RAM_HDRS} -) -target_link_libraries(esi_ram_test - ${CAPNP_LIBRARIES} - refl-cpp -) -target_include_directories(esi_ram_test PUBLIC - # Include the copied ESI C++ runtime headers. - ${PYCDE_OUT_DIR}/runtime/cpp/include) - -message("ESI_RAM_SRCS: ${ESI_RAM_SRCS}") -message("ESI_RAM_HDRS: ${ESI_RAM_HDRS}") - -target_include_directories( - esi_ram_test - PUBLIC - ${CMAKE_CURRENT_BINARY_DIR} - ${CMAKE_CURRENT_SOURCE_DIR} - ${CAPNP_INCLUDE_DIRS} - ${CAPNP_OUTDIR} - ${CIRCT_DIR}/include - ${ESI_HW_INCLUDE_DIR} -) - -target_compile_definitions( - esi_ram_test - PUBLIC - -DESI_COSIM_CAPNP_H=\"${CAPNPC_OUTPUT_DIR}/schema.capnp.h\" -) diff --git a/frontends/PyCDE/integration_test/esi_ram_cpp/esi_ram.cpp b/frontends/PyCDE/integration_test/esi_ram_cpp/esi_ram.cpp deleted file mode 100644 index a7573152de..0000000000 --- a/frontends/PyCDE/integration_test/esi_ram_cpp/esi_ram.cpp +++ /dev/null @@ -1,138 +0,0 @@ -// REQUIRES: esi-cosim -// XFAIL: * - -// clang-format off - -// Create ESI system -// RUN: rm -rf %t -// RUN: %PYTHON% %S/../esi_ram.py %t 2>&1 - -// Build the project using the CMakeLists.txt from this directory. Just move -// everything to the output folder in the build directory; this is very convenient -// if we want to run the build manually afterwards. -// RUN: cp %s %t -// RUN: cp %S/CMakeLists.txt %t -// RUN: cmake -S %t \ -// RUN: -B %t/build \ -// RUN: -DCIRCT_DIR=%CIRCT_SOURCE% \ -// RUN: -DPYCDE_OUT_DIR=%t -// RUN: cmake --build %t/build - -// Run test -// ... can't glob *.sv because PyCDE always includes driver.sv, but that's not the -// top that we want to use. Just delete it. -// RUN: rm %t/hw/driver.sv -// RUN: esi-cosim-runner.py --tmpdir=%t \ -// RUN: --no-aux-files \ -// RUN: --schema %t/hw/schema.capnp \ -// RUN: --exec %t/build/esi_ram_test \ -// RUN: %t/hw/*.sv - -// To run this test manually: -// 1. run `ninja check-pycde-integration` (this will create the output folder, run PyCDE, ...) -// 2. navigate to %t -// 3. In a separate terminal, run esi-cosim-runner.py in server only mode: -// - cd %t -// - esi-cosim-runner.py --tmpdir=$(pwd) --schema=$(pwd)/hw/schema.capnp --server-only $(pwd)/hw/top.sv $(pwd) -// 4. In another terminal, run the test executable. When running esi-cosim-runner, it'll print the $port which -// the test executable should connect to. -// - cd %t/build -// - ./esi_ram_test localhost:$port ../hw/schema.capn - -// clang-format on -#include -#include -#include -#include - -#include "esi/backends/capnp.h" - -#include ESI_COSIM_CAPNP_H - -#include "ESISystem.h" - -using namespace esi; -using namespace runtime; - -template -int logTestFailure(T expected, T actual, int testID) { - std::cerr << "Test " << testID << " failed: expected " << expected << ", got " - << actual << std::endl; - return testID; -} - -template -int runTest(TBackend &backend) { - // Connect the ESI system to the provided backend. - esi::runtime::top top(backend); - - auto write_cmd = - ESITypes::Struct16871797234873963366{.address = 2, .data = 42}; - - auto loopback_result = (*top.bsp->loopback)(write_cmd); - if (loopback_result != write_cmd) - return logTestFailure(write_cmd, loopback_result, 1); - - auto read_result = (*top.bsp->read)(2); - if (read_result != ESITypes::I64(0)) - return logTestFailure(ESITypes::I64(0), read_result, 2); - - read_result = (*top.bsp->read)(3); - if (read_result != ESITypes::I64(0)) - return logTestFailure(ESITypes::I64(0), read_result, 3); - - (*top.bsp->write)(write_cmd); - read_result = (*top.bsp->read)(2); - if (read_result != ESITypes::I64(42)) - return logTestFailure(ESITypes::I64(42), read_result, 4); - - read_result = (*top.bsp->read)(3); - if (read_result != ESITypes::I64(42)) - return logTestFailure(ESITypes::I64(42), read_result, 5); - - // Re-write a 0 to the memory (mostly for debugging purposes to allow us to - // keep the server alive and rerun the test). - write_cmd = ESITypes::Struct16871797234873963366{.address = 2, .data = 0}; - (*top.bsp->write)(write_cmd); - read_result = (*top.bsp->read)(2); - if (read_result != ESITypes::I64(0)) - return logTestFailure(ESITypes::I64(0), read_result, 6); - - return 0; -} - -int run_cosim_test(const std::string &host, unsigned port) { - // Run test with cosimulation backend. - esi::runtime::cosim::CapnpBackend cosim(host, port); - return runTest(cosim); -} - -int main(int argc, char **argv) { - std::string rpchostport; - if (argc != 3) { - // Schema not currently used but required by the ESI cosim tester - std::cerr - << "usage: esi_ram_test {rpc hostname}:{rpc port} {path to schema}" - << std::endl; - return 1; - } - - rpchostport = argv[1]; - - // Parse the RPC host and port from the command line. - auto colon = rpchostport.find(':'); - if (colon == std::string::npos) { - std::cerr << "Invalid RPC host:port string: " << rpchostport << std::endl; - return 1; - } - auto host = rpchostport.substr(0, colon); - auto port = stoi(rpchostport.substr(colon + 1)); - - auto res = run_cosim_test(host, port); - if (res != 0) { - std::cerr << "Test failed with error code " << res << std::endl; - return 1; - } - std::cout << "Test passed" << std::endl; - return 0; -} diff --git a/frontends/PyCDE/integration_test/esi_ram_cpp/lit.local.cfg b/frontends/PyCDE/integration_test/esi_ram_cpp/lit.local.cfg deleted file mode 100644 index 81261555b4..0000000000 --- a/frontends/PyCDE/integration_test/esi_ram_cpp/lit.local.cfg +++ /dev/null @@ -1 +0,0 @@ -config.suffixes.add('.cpp') diff --git a/frontends/PyCDE/src/CMakeLists.txt b/frontends/PyCDE/src/CMakeLists.txt index a2e90da5db..c1f1d72ed6 100644 --- a/frontends/PyCDE/src/CMakeLists.txt +++ b/frontends/PyCDE/src/CMakeLists.txt @@ -8,10 +8,6 @@ include(AddMLIRPython) -# Some of the PyCDE CMake code which is only used in Capnp builds uses newer -# CMake features. -cmake_minimum_required(VERSION 3.21.0) - add_compile_definitions("MLIR_PYTHON_PACKAGE_PREFIX=pycde.circt.") declare_mlir_python_sources(PyCDESources @@ -39,19 +35,13 @@ declare_mlir_python_sources(PyCDESources signals.py ndarray.py esi.py - esi_api.py - esi_runtime_common.py fsm.py testing.py - esi_api.py.j2 - Makefile.cosim - bsp/__init__.py bsp/common.py bsp/cosim.py bsp/xrt.py - bsp/EsiXrtPython.cpp bsp/Makefile.xrt.mk bsp/xrt_package.tcl bsp/xrt_api.py @@ -126,32 +116,3 @@ foreach(CFile IN LISTS CollateralFiles) COMPONENT PyCDE ) endforeach() - -install(TARGETS circt-std-sim-drivers - PUBLIC_HEADER DESTINATION python_packages/pycde/collateral - COMPONENT PyCDE -) -if(ESI_COSIM) - add_dependencies(PyCDE EsiCosimDpiServer) - set_property(TARGET EsiCosimDpiServer PROPERTY INSTALL_RPATH "$ORIGIN") - install(TARGETS EsiCosimDpiServer - RUNTIME_DEPENDENCY_SET EsiCosimDpiServer_RUNTIME_DEPS - DESTINATION python_packages/pycde/collateral - COMPONENT PyCDE - ) - install(RUNTIME_DEPENDENCY_SET EsiCosimDpiServer_RUNTIME_DEPS - DESTINATION python_packages/pycde/collateral - PRE_EXCLUDE_REGEXES .* - PRE_INCLUDE_REGEXES capnp kj - COMPONENT PyCDE - ) - install(TARGETS MtiPli - DESTINATION python_packages/pycde/collateral - COMPONENT PyCDE - ) - install(FILES - "$/CosimDpi.capnp" - DESTINATION python_packages/pycde/collateral/runtime - COMPONENT PyCDE - ) -endif() diff --git a/frontends/PyCDE/src/Makefile.cosim b/frontends/PyCDE/src/Makefile.cosim deleted file mode 100644 index e26c7a08da..0000000000 --- a/frontends/PyCDE/src/Makefile.cosim +++ /dev/null @@ -1,23 +0,0 @@ -mkfile_path := $(abspath $(lastword $(MAKEFILE_LIST))) -mkfile_dir := $(dir $(mkfile_path)) - -VSIM_PATH := $(which vsim) - -ifneq ($(VSIM_PATH),) - QUESTA_PATH := $(basename $(VSIM_PATH)) -endif - -ifneq ($(QUESTA_PATH),) -run_questa: - $(QUESTA_PATH)/vlog hw/*.sv - $(QUESTA_PATH)/vsim driver -c -sv_lib hw/libEsiCosimDpiServer -do "run -all; quit" -endif - -VERILATOR_PATH := $(which verilator) -ifneq ($(VERILATOR_PATH),) -SV_SRCS = $(shell ls hw/*.sv | grep -v driver.sv) -VERILATOR_SRCS = $(SV_SRCS) $(mkfile_dir)/hw/*.so hw/*.cpp -run_verilator: - $(VERILATOR_PATH) --cc --top-module top -sv --build --exe --assert $(VERILATOR_SRCS) - LD_LIBRARY_PATH=hw obj_dir/Vtop -endif diff --git a/frontends/PyCDE/src/bsp/EsiXrtPython.cpp b/frontends/PyCDE/src/bsp/EsiXrtPython.cpp deleted file mode 100644 index 1fad82a04a..0000000000 --- a/frontends/PyCDE/src/bsp/EsiXrtPython.cpp +++ /dev/null @@ -1,58 +0,0 @@ -#include -#include -#include -#include -#include - -// pybind11 includes -#include "pybind11/pybind11.h" -#include "pybind11/stl.h" -namespace py = pybind11; - -// XRT includes -#include "experimental/xrt_bo.h" -#include "experimental/xrt_device.h" -#include "experimental/xrt_ip.h" -#include "experimental/xrt_xclbin.h" - -// We don't want to clutter up the symbol space any more than necessary, so use -// an anonymous namespace. -namespace { - -uint32_t MagicNumOffset = 16; -uint32_t MagicNumberLo = 0xE5100E51; -uint32_t MagicNumberHi = 0x207D98E5; -uint32_t ExpectedVersionNumber = 0; - -class Accelerator { - xrt::device m_device; - xrt::ip m_ip; - -public: - Accelerator(const std::string &xclbin_path, const std::string kernel_name) { - m_device = xrt::device(0); - auto uuid = m_device.load_xclbin(xclbin_path); - m_ip = xrt::ip(m_device, uuid, kernel_name); - - // Check that this is actually an ESI system. - uint32_t magicLo = m_ip.read_register(MagicNumOffset); - uint32_t magicHi = m_ip.read_register(MagicNumOffset + 4); - if (magicLo != MagicNumberLo || magicHi != MagicNumberHi) - throw std::runtime_error("Accelerator is not an ESI system"); - - // Check version is one we understand. - if (version() != ExpectedVersionNumber) - std::cerr - << "[ESI] Warning: accelerator ESI version may not be compatible\n"; - } - - uint32_t version() { return m_ip.read_register(MagicNumOffset + 8); } -}; - -} // namespace - -PYBIND11_MODULE(esiXrtPython, m) { - py::class_(m, "Accelerator") - .def(py::init()) - .def("version", &Accelerator::version); -} diff --git a/frontends/PyCDE/src/esi_api.py b/frontends/PyCDE/src/esi_api.py deleted file mode 100644 index 64b1ae018e..0000000000 --- a/frontends/PyCDE/src/esi_api.py +++ /dev/null @@ -1,157 +0,0 @@ -# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -# See https://llvm.org/LICENSE.txt for license information. -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -from jinja2 import Environment, FileSystemLoader, StrictUndefined - -from io import FileIO -import json -import pathlib -import re -import shutil -from typing import Dict, List - -__dir__ = pathlib.Path(__file__).parent - - -def _camel_to_snake(camel: str): - if camel.upper() == camel: - return camel.lower() - return re.sub(r'(? bool: - """Is a Python object compatible with HW type.""" - assert False, "unimplemented" - - -class VoidType(Type): - - def __init__(self, type_id: typing.Optional[int] = None): - super().__init__(0, type_id) - - def is_valid(self, obj) -> bool: - return obj is None - - -class IntType(Type): - - def __init__(self, - width: int, - signed: bool, - type_id: typing.Optional[int] = None): - super().__init__(width, type_id) - self.signed = signed - - def is_valid(self, obj) -> bool: - if self.width == 0: - return obj is None - if not isinstance(obj, int): - return False - if obj >= 2**self.width: - return False - return True - - def __str__(self): - return ("" if self.signed else "u") + \ - f"int{self.width}" - - -class StructType(Type): - - def __init__(self, - fields: typing.List[typing.Tuple[str, Type]], - type_id: typing.Optional[int] = None): - self.fields = fields - width = sum([ftype.width for (_, ftype) in self.fields]) - super().__init__(width, type_id) - - def is_valid(self, obj) -> bool: - fields_count = 0 - if isinstance(obj, dict): - for (fname, ftype) in self.fields: - if fname not in obj: - return False - if not ftype.is_valid(obj[fname]): - return False - fields_count += 1 - if fields_count != len(obj): - return False - return True - return False - - -class Port: - - def __init__(self, - client_path: typing.List[str], - backend, - impl_type: str, - read_type: typing.Optional[Type] = None, - write_type: typing.Optional[Type] = None): - # If a backend doesn't support a particular implementation type, just skip - # it. We don't want to error out on services which aren't being used. - if backend.supports_impl(impl_type): - self._backend = backend.get_port(client_path, read_type, write_type) - else: - self._backend = None - self.client_path = client_path - self.read_type = read_type - self.write_type = write_type - - -class WritePort(Port): - - def write(self, msg=None) -> bool: - assert self.write_type is not None, "Expected non-None write_type" - if not self.write_type.is_valid(msg): - raise ValueError(f"'{msg}' cannot be converted to '{self.write_type}'") - if self._backend is None: - raise ValueError("Backend does not support implementation of port") - return self._backend.write(msg) - - -class ReadPort(Port): - - def read(self, blocking_timeout: typing.Optional[float] = 1.0): - if self._backend is None: - raise ValueError("Backend does not support implementation of port") - return self._backend.read(blocking_timeout) - - -class ReadWritePort(Port): - - def __call__(self, - msg=None, - blocking_timeout: typing.Optional[float] = 1.0) -> typing.Any: - """Send a message and wait for a response. If 'timeout' is exceeded while - waiting for a response, there may well be one coming. It is the caller's - responsibility to clear the response channel before sending another request - so as to ensure correlation between request and response. - - Intended for blocking or synchronous interfaces.""" - - if not self.write(msg): - raise RuntimeError(f"Could not send message '{msg}'") - return self.read(blocking_timeout) - - def write(self, msg=None) -> bool: - assert self.write_type is not None, "Expected non-None write_type" - if not self.write_type.is_valid(msg): - raise ValueError(f"'{msg}' cannot be converted to '{self.write_type}'") - return self._backend.write(msg) - - def read(self, blocking_timeout: typing.Optional[float] = 1.0): - return self._backend.read(blocking_timeout) - - -class _CosimNode: - """Provides a capnp-based co-simulation backend.""" - - def __init__(self, root, prefix: typing.List[str]): - self._root: Cosim = root - self._endpoint_prefix = prefix - - def supports_impl(self, impl_type: str) -> bool: - """The cosim backend only supports cosim connectivity implementations.""" - return impl_type == "cosim" - - def get_child(self, child_name: str): - """When instantiating a child instance, get the backend node with which it - is associated.""" - child_path = self._endpoint_prefix + [child_name] - return _CosimNode(self._root, child_path) - - def get_port(self, - client_path: typing.List[str], - read_type: typing.Optional[Type] = None, - write_type: typing.Optional[Type] = None): - """When building a service port, get the backend port which it should use - for interactions.""" - path = ".".join(self._endpoint_prefix) + "." + "_".join(client_path) - ep = self._root._open_endpoint( - path, - write_type=write_type.type_id if write_type is not None else None, - read_type=read_type.type_id if read_type is not None else None) - return _CosimPort(self, ep, read_type, write_type) - - -class Cosim(_CosimNode): - """Connect to a Cap'N Proto RPC co-simulation and provide a cosim backend - service.""" - - def __init__(self, schemaPath, hostPort): - """Load the schema and connect to the RPC server""" - self._schema = capnp.load(schemaPath) - self._rpc_client = capnp.TwoPartyClient(hostPort) - self._cosim = self._rpc_client.bootstrap().cast_as( - self._schema.CosimDpiServer) - - # Find the simulation prefix and use it in our parent constructor. - ifaces = self.list() - prefix = [] if len(ifaces) == 0 else ifaces[0].endpointID.split(".")[:1] - super().__init__(self, prefix) - - def load_package(path: os.PathLike): - """Load a cosim connection from something running out of 'path' package dir. - Reads and parses 'cosim.cfg' from that directory to get the connection - information. Loads the capnp schema from the 'runtime' directory in that - package path.""" - path = Path(path) - simcfg = path / "cosim.cfg" - if not simcfg.exists(): - simcfg = Path.cwd() / "cosim.cfg" - if not simcfg.exists(): - raise RuntimeError("Could not find simulation connection file") - port_lines = filter(lambda x: x.startswith("port:"), - simcfg.open().readlines()) - port = int(list(port_lines)[0].split(":")[1]) - return Cosim(os.path.join(path, "runtime", "schema.capnp"), - f"{os.uname()[1]}:{port}") - - def list(self): - """List the available interfaces""" - return self._cosim.list().wait().ifaces - - def _open_endpoint(self, epid: str, write_type=None, read_type=None): - """Open the endpoint, optionally checking the send and recieve types""" - for iface in self.list(): - if iface.endpointID == epid: - # Optionally check that the type IDs match. - if write_type is not None: - assert iface.sendTypeID == write_type.schema.node.id - else: - assert write_type is None - if read_type is not None: - assert iface.recvTypeID == read_type.schema.node.id - else: - assert read_type is None - - openResp = self._cosim.open(iface).wait() - assert openResp.iface is not None - return openResp.iface - assert False, f"Could not find specified EndpointID: {epid}" - - -class _CosimPort: - """Cosim backend for service ports. This is where the real meat is buried.""" - - class _TypeConverter: - """Parent class for Capnp type converters.""" - - def __init__(self, schema, esi_type: Type): - self.esi_type = esi_type - assert hasattr(esi_type, "capnp_name") - if not hasattr(schema, esi_type.capnp_name): - raise ValueError("Cosim does not support non-capnp types.") - self.capnp_type = getattr(schema, esi_type.capnp_name) - - class _VoidConverter(_TypeConverter): - """Convert python ints to and from capnp messages.""" - - def write(self, py_int: None): - return self.capnp_type.new_message() - - def read(self, capnp_resp) -> None: - return capnp_resp.as_struct(self.capnp_type) - - class _IntConverter(_TypeConverter): - """Convert python ints to and from capnp messages.""" - - def write(self, py_int: int): - return self.capnp_type.new_message(i=py_int) - - def read(self, capnp_resp) -> int: - return capnp_resp.as_struct(self.capnp_type).i - - class _StructConverter(_TypeConverter): - """Convert python ints to and from capnp messages.""" - - def write(self, py_dict: dict): - return self.capnp_type.new_message(**py_dict) - - def read(self, capnp_resp) -> int: - capnp_msg = capnp_resp.as_struct(self.capnp_type) - ret = {} - for (fname, _) in self.esi_type.fields: - if hasattr(capnp_msg, fname): - ret[fname] = getattr(capnp_msg, fname) - return ret - - # Lookup table for getting the correct type converter for a given type. - ConvertLookup = { - VoidType: _VoidConverter, - IntType: _IntConverter, - StructType: _StructConverter - } - - def __init__(self, node: _CosimNode, endpoint, - read_type: typing.Optional[Type], - write_type: typing.Optional[Type]): - self._endpoint = endpoint - schema = node._root._schema - # For each type, lookup the type converter and store that instead of the - # type itself. - if read_type is not None: - converter = _CosimPort.ConvertLookup[type(read_type)] - self._read_convert = converter(schema, read_type) - if write_type is not None: - converter = _CosimPort.ConvertLookup[type(write_type)] - self._write_convert = converter(schema, write_type) - - def write(self, msg) -> bool: - """Write a message to this port.""" - self._endpoint.send(self._write_convert.write(msg)).wait() - return True - - def read(self, blocking_time: typing.Optional[float]): - """Read a message from this port. If 'blocking_timeout' is None, return - immediately. Otherwise, wait up to 'blocking_timeout' for a message. Returns - the message if found, None if no message was read.""" - - if blocking_time is None: - # Non-blocking. - recvResp = self._endpoint.recv(False).wait() - else: - # Blocking. Since our cosim rpc server doesn't currently support blocking - # reads, use polling instead. - e = time.time() + blocking_time - recvResp = None - while recvResp is None or e > time.time(): - recvResp = self._endpoint.recv(False).wait() - if recvResp.hasData: - break - else: - time.sleep(0.001) - if not recvResp.hasData: - return None - assert recvResp.resp is not None - return self._read_convert.read(recvResp.resp) diff --git a/frontends/PyCDE/src/system.py b/frontends/PyCDE/src/system.py index f6869892f1..8d01fc2a1b 100644 --- a/frontends/PyCDE/src/system.py +++ b/frontends/PyCDE/src/system.py @@ -15,7 +15,6 @@ from .types import TypeAlias from . import circt from .circt import ir, passmanager from .circt.dialects import esi, hw, msft -from .esi_api import PythonApiBuilder from contextvars import ContextVar from collections.abc import Iterable