diff --git a/integration_test/Dialect/ESI/runtime/basic_mmio.sv.py b/integration_test/Dialect/ESI/runtime/basic_mmio.sv.py index 9a883f771e6f..15f6e79d67c5 100644 --- a/integration_test/Dialect/ESI/runtime/basic_mmio.sv.py +++ b/integration_test/Dialect/ESI/runtime/basic_mmio.sv.py @@ -1,8 +1,7 @@ import esi -import os import sys -acc = esi.Accelerator(sys.argv[1], sys.argv[2]) +acc = esi.AcceleratorConnection(sys.argv[1], sys.argv[2]) mmio = acc.get_service_mmio() diff --git a/integration_test/Dialect/ESI/runtime/loopback.mlir.py b/integration_test/Dialect/ESI/runtime/loopback.mlir.py index ffc57b5e03e2..662dcc6ea21b 100644 --- a/integration_test/Dialect/ESI/runtime/loopback.mlir.py +++ b/integration_test/Dialect/ESI/runtime/loopback.mlir.py @@ -3,7 +3,7 @@ import sys platform = sys.argv[1] -acc = esi.Accelerator(platform, sys.argv[2]) +acc = esi.AcceleratorConnection(platform, sys.argv[2]) assert acc.sysinfo().esi_version() == 1 m = acc.manifest() @@ -36,7 +36,7 @@ def strType(t: esi.Type) -> str: print(f"{esiType}:") print(f" {strType(esiType)}") -d = m.build_design(acc) +d = m.build_accelerator(acc) loopback = d.children[esi.AppID("loopback_inst", 0)] appid = loopback.id diff --git a/lib/Dialect/ESI/CMakeLists.txt b/lib/Dialect/ESI/CMakeLists.txt index 51f9f9de7c02..9533448fd809 100644 --- a/lib/Dialect/ESI/CMakeLists.txt +++ b/lib/Dialect/ESI/CMakeLists.txt @@ -72,19 +72,20 @@ set(ESI_RUNTIME_SRCS runtime/cpp/include/esi/backends/Cosim.h runtime/cpp/include/esi/backends/Trace.h + runtime/cpp/include/esi/Utils.h runtime/cpp/include/esi/Accelerator.h - runtime/cpp/include/esi/Design.h runtime/cpp/include/esi/Manifest.h - runtime/cpp/include/esi/StdServices.h runtime/cpp/include/esi/Types.h - runtime/cpp/include/esi/Utils.h + runtime/cpp/include/esi/Ports.h + runtime/cpp/include/esi/Services.h + runtime/cpp/include/esi/Common.h runtime/cpp/lib/backends/Cosim.cpp runtime/cpp/lib/backends/Trace.cpp - runtime/cpp/lib/Accelerator.cpp - runtime/cpp/lib/Design.cpp - runtime/cpp/lib/Manifest.cpp - runtime/cpp/lib/StdServices.cpp runtime/cpp/lib/Utils.cpp + runtime/cpp/lib/Manifest.cpp + runtime/cpp/lib/Services.cpp + runtime/cpp/lib/Accelerator.cpp + runtime/cpp/lib/Ports.cpp runtime/cpp/tools/esiquery.cpp runtime/cosim/CMakeLists.txt runtime/cosim/Cosim_MMIO.sv @@ -100,10 +101,10 @@ set(ESI_RUNTIME_SRCS runtime/cosim/include/cosim/Server.h runtime/cosim/include/cosim/dpi.h runtime/cosim/include/dpi/svdpi.h - runtime/cosim/Cosim_Endpoint.sv - runtime/cosim/CosimDpi.capnp runtime/cosim/Cosim_DpiPkg.sv runtime/cosim/Cosim_Manifest.sv + runtime/cosim/Cosim_Endpoint.sv + runtime/cosim/CosimDpi.capnp runtime/python/esi/__init__.py runtime/python/esi/accelerator.py runtime/python/esi/esiCppAccel.cpp diff --git a/lib/Dialect/ESI/runtime/CMakeLists.txt b/lib/Dialect/ESI/runtime/CMakeLists.txt index 678778967aaa..c1b4df0f09db 100644 --- a/lib/Dialect/ESI/runtime/CMakeLists.txt +++ b/lib/Dialect/ESI/runtime/CMakeLists.txt @@ -44,9 +44,9 @@ include_directories(cpp/include) set(ESIRuntimeSources cpp/lib/Accelerator.cpp - cpp/lib/Design.cpp cpp/lib/Manifest.cpp - cpp/lib/StdServices.cpp + cpp/lib/Services.cpp + cpp/lib/Ports.cpp cpp/lib/Utils.cpp cpp/lib/backends/Trace.cpp diff --git a/lib/Dialect/ESI/runtime/cpp/include/esi/Accelerator.h b/lib/Dialect/ESI/runtime/cpp/include/esi/Accelerator.h index 694a3667f7c4..1cb7853a23fb 100644 --- a/lib/Dialect/ESI/runtime/cpp/include/esi/Accelerator.h +++ b/lib/Dialect/ESI/runtime/cpp/include/esi/Accelerator.h @@ -21,8 +21,9 @@ #ifndef ESI_ACCELERATOR_H #define ESI_ACCELERATOR_H -#include "esi/Design.h" #include "esi/Manifest.h" +#include "esi/Ports.h" +#include "esi/Services.h" #include #include @@ -34,92 +35,89 @@ namespace esi { +//===----------------------------------------------------------------------===// +// Constants used by low-level APIs. +//===----------------------------------------------------------------------===// + constexpr uint32_t MagicNumOffset = 16; constexpr uint32_t MagicNumberLo = 0xE5100E51; constexpr uint32_t MagicNumberHi = 0x207D98E5; constexpr uint32_t VersionNumberOffset = MagicNumOffset + 8; constexpr uint32_t ExpectedVersionNumber = 0; -/// Unidirectional channels are the basic communication primitive between the -/// host and accelerator. A 'ChannelPort' is the host side of a channel. It can -/// be either read or write but not both. At this level, channels are untyped -- -/// just streams of bytes. They are not intended to be used directly by users -/// but used by higher level APIs which add types. -class ChannelPort { -public: - ChannelPort(const Type &type) : type(type) {} - virtual ~ChannelPort() = default; - - virtual void connect() {} - virtual void disconnect() {} - - const Type &getType() const { return type; } +//===----------------------------------------------------------------------===// +// Accelerator design hierarchy. +//===----------------------------------------------------------------------===// -private: - const Type &type; -}; +class Instance; -/// A ChannelPort which sends data to the accelerator. -class WriteChannelPort : public ChannelPort { +class HWModule { public: - using ChannelPort::ChannelPort; + HWModule(std::optional info, + std::vector> children, + std::vector services, + std::vector ports); + + std::optional getInfo() const { return info; } + const std::vector> &getChildrenOrdered() const { + return children; + } + const std::map &getChildren() const { return childIndex; } + const std::vector &getPortsOrdered() const { return ports; } + const std::map &getPorts() const { + return portIndex; + } - /// A very basic write API. Will likely change for performance reasons. - virtual void write(const void *data, size_t size) = 0; +protected: + const std::optional info; + const std::vector> children; + const std::map childIndex; + const std::vector services; + const std::vector ports; + const std::map portIndex; }; -/// A ChannelPort which reads data from the accelerator. -class ReadChannelPort : public ChannelPort { +class Instance : public HWModule { public: - using ChannelPort::ChannelPort; + Instance() = delete; + Instance(const Instance &) = delete; + ~Instance() = default; + Instance(AppID id, std::optional info, + std::vector> children, + std::vector services, + std::vector ports) + : HWModule(info, std::move(children), services, ports), id(id) {} - /// Specify a buffer to read into and a maximum size to read. Returns the - /// number of bytes read, or -1 on error. Basic API, will likely change for - /// performance reasons. - virtual std::ptrdiff_t read(void *data, size_t maxSize) = 0; -}; - -namespace services { -/// Parent class of all APIs modeled as 'services'. May or may not map to a -/// hardware side 'service'. -class Service { -public: - using Type = const std::type_info &; - virtual ~Service() = default; + const AppID getID() const { return id; } - virtual std::string getServiceSymbol() const = 0; +protected: + const AppID id; }; -/// A service for which there are no standard services registered. Requires -/// ports be added to the design hierarchy instead of high level interfaces like -/// the ones in StdServices.h. -class CustomService : public Service { +class Accelerator : public HWModule { public: - CustomService(AppIDPath idPath, const ServiceImplDetails &details, - const HWClientDetails &clients); - virtual ~CustomService() = default; - - virtual std::string getServiceSymbol() const override { - return serviceSymbol; - } + Accelerator() = delete; + Accelerator(const Accelerator &) = delete; + ~Accelerator() = default; + Accelerator(std::optional info, + std::vector> children, + std::vector services, + std::vector ports, + std::shared_ptr manifestImpl) + : HWModule(info, std::move(children), services, ports), + manifestImpl(manifestImpl) {} - /// Request the host side channel ports for a particular instance (identified - /// by the AppID path). For convenience, provide the bundle type and direction - /// of the bundle port. - virtual std::map - requestChannelsFor(AppIDPath, const BundleType &, - BundlePort::Direction portDir) = 0; - -protected: - std::string serviceSymbol; - AppIDPath id; +private: + std::shared_ptr manifestImpl; }; -} // namespace services -/// An ESI accelerator system. -class Accelerator { +//===----------------------------------------------------------------------===// +// Connection to the accelerator and its services. +//===----------------------------------------------------------------------===// + +class AcceleratorConnection { public: - virtual ~Accelerator() = default; + virtual ~AcceleratorConnection() = default; using Service = services::Service; /// Get a typed reference to a particular service type. Caller does *not* take @@ -159,13 +157,14 @@ namespace registry { // Connect to an ESI accelerator given a backend name and connection specifier. // Alternatively, instantiate the backend directly (if you're using C++). -std::unique_ptr connect(std::string backend, - std::string connection); +std::unique_ptr connect(std::string backend, + std::string connection); namespace internal { /// Backends can register themselves to be connected via a connection string. -using BackendCreate = std::function(std::string)>; +using BackendCreate = + std::function(std::string)>; void registerBackend(std::string name, BackendCreate create); // Helper struct to diff --git a/lib/Dialect/ESI/runtime/cpp/include/esi/Common.h b/lib/Dialect/ESI/runtime/cpp/include/esi/Common.h new file mode 100644 index 000000000000..62667444a8c9 --- /dev/null +++ b/lib/Dialect/ESI/runtime/cpp/include/esi/Common.h @@ -0,0 +1,88 @@ +//===- Common.h - Commonly used classes w/o dependencies --------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// DO NOT EDIT! +// This file is distributed as part of an ESI package. The source for this file +// should always be modified within CIRCT. +// +//===----------------------------------------------------------------------===// + +// NOLINTNEXTLINE(llvm-header-guard) +#ifndef ESI_COMMON_H +#define ESI_COMMON_H + +#include +#include +#include +#include +#include +#include + +namespace esi { + +//===----------------------------------------------------------------------===// +// Common accelerator description types. +//===----------------------------------------------------------------------===// + +struct AppID { + std::string name; + std::optional idx; + + AppID(const AppID &) = default; + AppID(const std::string &name, std::optional idx = std::nullopt) + : name(name), idx(idx) {} + + bool operator==(const AppID &other) const { + return name == other.name && idx == other.idx; + } + bool operator!=(const AppID &other) const { return !(*this == other); } +}; +bool operator<(const AppID &a, const AppID &b); + +class AppIDPath : public std::vector { +public: + using std::vector::vector; + + AppIDPath operator+(const AppIDPath &b); + std::string toStr() const; +}; +bool operator<(const AppIDPath &a, const AppIDPath &b); +std::ostream &operator<<(std::ostream &, const esi::AppIDPath &); + +struct ModuleInfo { + const std::optional name; + const std::optional summary; + const std::optional version; + const std::optional repo; + const std::optional commitHash; + const std::map extra; +}; + +/// A description of a service port. Used pretty exclusively in setting up the +/// design. +struct ServicePortDesc { + std::string name; + std::string portName; +}; + +/// A description of a hardware client. Used pretty exclusively in setting up +/// the design. +struct HWClientDetail { + AppIDPath relPath; + ServicePortDesc port; + std::map implOptions; +}; +using HWClientDetails = std::vector; +using ServiceImplDetails = std::map; + +} // namespace esi + +std::ostream &operator<<(std::ostream &, const esi::ModuleInfo &); +std::ostream &operator<<(std::ostream &, const esi::AppID &); + +#endif // ESI_COMMON_H diff --git a/lib/Dialect/ESI/runtime/cpp/include/esi/Design.h b/lib/Dialect/ESI/runtime/cpp/include/esi/Design.h deleted file mode 100644 index 9669fdaf29d0..000000000000 --- a/lib/Dialect/ESI/runtime/cpp/include/esi/Design.h +++ /dev/null @@ -1,127 +0,0 @@ -//===- Design.h - Dynamic accelerator API -----------------------*- C++ -*-===// -// -// 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 -// -//===----------------------------------------------------------------------===// -// -// The dynamic API into an accelerator allows access to the accelerator's design -// and communication channels through various stl containers (e.g. std::vector, -// std::map, etc.). This allows runtime reflection against the accelerator and -// can be pybind'd to create a Python API. -// -// The static API, in contrast, is a compile-time API that allows access to the -// design and communication channels symbolically. It will be generated once -// (not here) then compiled into the host software. -// -// DO NOT EDIT! -// This file is distributed as part of an ESI package. The source for this file -// should always be modified within CIRCT. -// -//===----------------------------------------------------------------------===// - -// NOLINTNEXTLINE(llvm-header-guard) -#ifndef ESI_DESIGN_H -#define ESI_DESIGN_H - -#include "esi/Manifest.h" - -#include -#include -#include - -namespace esi { -// Forward declarations. -class Instance; -class ChannelPort; -class WriteChannelPort; -class ReadChannelPort; -namespace services { -class Service; -} - -/// Services provide connections to 'bundles' -- collections of named, -/// unidirectional communication channels. This class provides access to those -/// ChannelPorts. -class BundlePort { -public: - /// Direction of a bundle. This -- combined with the channel direction in the - /// bundle -- can be used to determine if a channel should be writing to or - /// reading from the accelerator. - enum Direction { ToServer, ToClient }; - - /// Compute the direction of a channel given the bundle direction and the - /// bundle port's direction. - static bool isWrite(BundleType::Direction bundleDir, Direction svcDir) { - if (svcDir == Direction::ToClient) - return bundleDir == BundleType::Direction::To; - return bundleDir == BundleType::Direction::From; - } - - /// Construct a port. - BundlePort(AppID id, std::map channels); - - /// Get the ID of the port. - AppID getID() const { return _id; } - - /// Get access to the raw byte streams of a channel. Intended for internal - /// usage and binding to other languages (e.g. Python) which have their own - /// message serialization code. - WriteChannelPort &getRawWrite(const std::string &name) const; - ReadChannelPort &getRawRead(const std::string &name) const; - const std::map &getChannels() const { - return _channels; - } - -private: - AppID _id; - std::map _channels; -}; - -class Design { -public: - Design(std::optional info, - std::vector> children, - std::vector services, - std::vector ports); - - std::optional getInfo() const { return info; } - const std::vector> &getChildrenOrdered() const { - return children; - } - const std::map &getChildren() const { return childIndex; } - const std::vector &getPortsOrdered() const { return ports; } - const std::map &getPorts() const { - return portIndex; - } - -protected: - const std::optional info; - const std::vector> children; - const std::map childIndex; - const std::vector services; - const std::vector ports; - const std::map portIndex; -}; - -class Instance : public Design { -public: - Instance() = delete; - Instance(const Instance &) = delete; - ~Instance() = default; - Instance(AppID id, std::optional info, - std::vector> children, - std::vector services, - std::vector ports) - : Design(info, std::move(children), services, ports), id(id) {} - - const AppID getID() const { return id; } - -protected: - const AppID id; -}; - -} // namespace esi - -#endif // ESI_DESIGN_H diff --git a/lib/Dialect/ESI/runtime/cpp/include/esi/Manifest.h b/lib/Dialect/ESI/runtime/cpp/include/esi/Manifest.h index 18b7c35500ba..01b1c723860b 100644 --- a/lib/Dialect/ESI/runtime/cpp/include/esi/Manifest.h +++ b/lib/Dialect/ESI/runtime/cpp/include/esi/Manifest.h @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// // -// +// Manifest parsing and API creation. // // DO NOT EDIT! // This file is distributed as part of an ESI package. The source for this file @@ -18,6 +18,7 @@ #ifndef ESI_MANIFEST_H #define ESI_MANIFEST_H +#include "esi/Common.h" #include "esi/Types.h" #include @@ -31,69 +32,9 @@ namespace esi { -//===----------------------------------------------------------------------===// -// Common accelerator description types. -//===----------------------------------------------------------------------===// - -struct AppID { - std::string name; - std::optional idx; - - AppID(const AppID &) = default; - AppID(std::string name, std::optional idx = std::nullopt) - : name(name), idx(idx) {} - - bool operator==(const AppID &other) const { - return name == other.name && idx == other.idx; - } - bool operator!=(const AppID &other) const { return !(*this == other); } -}; -bool operator<(const AppID &a, const AppID &b); - -class AppIDPath : public std::vector { -public: - using std::vector::vector; - - AppIDPath operator+(const AppIDPath &b); - std::string toStr() const; -}; -bool operator<(const AppIDPath &a, const AppIDPath &b); -std::ostream &operator<<(std::ostream &, const esi::AppIDPath &); - -struct ModuleInfo { - const std::optional name; - const std::optional summary; - const std::optional version; - const std::optional repo; - const std::optional commitHash; - const std::map extra; -}; - -/// A description of a service port. Used pretty exclusively in setting up the -/// design. -struct ServicePortDesc { - std::string name; - std::string portName; -}; - -/// A description of a hardware client. Used pretty exclusively in setting up -/// the design. -struct HWClientDetail { - AppIDPath relPath; - ServicePortDesc port; - std::map implOptions; -}; -using HWClientDetails = std::vector; -using ServiceImplDetails = std::map; - -//===----------------------------------------------------------------------===// -// Manifest parsing and API creation. -//===----------------------------------------------------------------------===// - // Forward declarations. -namespace internal {} // namespace internal +class AcceleratorConnection; class Accelerator; -class Design; /// Class to parse a manifest. It also constructs the dynamic API for the /// accelerator. @@ -110,7 +51,8 @@ class Manifest { std::vector getModuleInfos() const; // Build a dynamic design hierarchy from the manifest. - std::unique_ptr buildDesign(Accelerator &acc) const; + std::unique_ptr + buildAccelerator(AcceleratorConnection &acc) const; /// Get a Type from the manifest based on its ID. Types are uniqued here. std::optional> getType(Type::ID id) const; @@ -122,7 +64,7 @@ class Manifest { const std::vector> &getTypeTable() const; private: - Impl &impl; + std::shared_ptr impl; }; } // namespace esi diff --git a/lib/Dialect/ESI/runtime/cpp/include/esi/Ports.h b/lib/Dialect/ESI/runtime/cpp/include/esi/Ports.h new file mode 100644 index 000000000000..934376438fa3 --- /dev/null +++ b/lib/Dialect/ESI/runtime/cpp/include/esi/Ports.h @@ -0,0 +1,103 @@ +//===- Ports.h - ESI communication channels ---------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// DO NOT EDIT! +// This file is distributed as part of an ESI package. The source for this file +// should always be modified within CIRCT. +// +//===----------------------------------------------------------------------===// + +// NOLINTNEXTLINE(llvm-header-guard) +#ifndef ESI_PORTS_H +#define ESI_PORTS_H + +#include "esi/Common.h" +#include "esi/Types.h" + +namespace esi { + +/// Unidirectional channels are the basic communication primitive between the +/// host and accelerator. A 'ChannelPort' is the host side of a channel. It can +/// be either read or write but not both. At this level, channels are untyped -- +/// just streams of bytes. They are not intended to be used directly by users +/// but used by higher level APIs which add types. +class ChannelPort { +public: + ChannelPort(const Type &type) : type(type) {} + virtual ~ChannelPort() = default; + + virtual void connect() {} + virtual void disconnect() {} + + const Type &getType() const { return type; } + +private: + const Type &type; +}; + +/// A ChannelPort which sends data to the accelerator. +class WriteChannelPort : public ChannelPort { +public: + using ChannelPort::ChannelPort; + + /// A very basic write API. Will likely change for performance reasons. + virtual void write(const void *data, size_t size) = 0; +}; + +/// A ChannelPort which reads data from the accelerator. +class ReadChannelPort : public ChannelPort { +public: + using ChannelPort::ChannelPort; + + /// Specify a buffer to read into and a maximum size to read. Returns the + /// number of bytes read, or -1 on error. Basic API, will likely change for + /// performance reasons. + virtual std::ptrdiff_t read(void *data, size_t maxSize) = 0; +}; + +/// Services provide connections to 'bundles' -- collections of named, +/// unidirectional communication channels. This class provides access to those +/// ChannelPorts. +class BundlePort { +public: + /// Direction of a bundle. This -- combined with the channel direction in the + /// bundle -- can be used to determine if a channel should be writing to or + /// reading from the accelerator. + enum Direction { ToServer, ToClient }; + + /// Compute the direction of a channel given the bundle direction and the + /// bundle port's direction. + static bool isWrite(BundleType::Direction bundleDir, Direction svcDir) { + if (svcDir == Direction::ToClient) + return bundleDir == BundleType::Direction::To; + return bundleDir == BundleType::Direction::From; + } + + /// Construct a port. + BundlePort(AppID id, std::map channels); + + /// Get the ID of the port. + AppID getID() const { return _id; } + + /// Get access to the raw byte streams of a channel. Intended for internal + /// usage and binding to other languages (e.g. Python) which have their own + /// message serialization code. + WriteChannelPort &getRawWrite(const std::string &name) const; + ReadChannelPort &getRawRead(const std::string &name) const; + const std::map &getChannels() const { + return _channels; + } + +private: + AppID _id; + std::map _channels; +}; + +} // namespace esi + +#endif // ESI_PORTS_H diff --git a/lib/Dialect/ESI/runtime/cpp/include/esi/StdServices.h b/lib/Dialect/ESI/runtime/cpp/include/esi/Services.h similarity index 62% rename from lib/Dialect/ESI/runtime/cpp/include/esi/StdServices.h rename to lib/Dialect/ESI/runtime/cpp/include/esi/Services.h index 718182984256..fed3091d534e 100644 --- a/lib/Dialect/ESI/runtime/cpp/include/esi/StdServices.h +++ b/lib/Dialect/ESI/runtime/cpp/include/esi/Services.h @@ -17,16 +17,52 @@ //===----------------------------------------------------------------------===// // NOLINTNEXTLINE(llvm-header-guard) -#ifndef ESI_RUNTIME_STDSERVICES_H -#define ESI_RUNTIME_STDSERVICES_H +#ifndef ESI_RUNTIME_SERVICES_H +#define ESI_RUNTIME_SERVICES_H -#include "esi/Accelerator.h" +#include "esi/Common.h" +#include "esi/Ports.h" #include namespace esi { namespace services { +/// Parent class of all APIs modeled as 'services'. May or may not map to a +/// hardware side 'service'. +class Service { +public: + using Type = const std::type_info &; + virtual ~Service() = default; + + virtual std::string getServiceSymbol() const = 0; +}; + +/// A service for which there are no standard services registered. Requires +/// ports be added to the design hierarchy instead of high level interfaces like +/// the ones in StdServices.h. +class CustomService : public Service { +public: + CustomService(AppIDPath idPath, const ServiceImplDetails &details, + const HWClientDetails &clients); + virtual ~CustomService() = default; + + virtual std::string getServiceSymbol() const override { + return serviceSymbol; + } + + /// Request the host side channel ports for a particular instance (identified + /// by the AppID path). For convenience, provide the bundle type and direction + /// of the bundle port. + virtual std::map + requestChannelsFor(AppIDPath, const BundleType &, + BundlePort::Direction portDir) = 0; + +protected: + std::string serviceSymbol; + AppIDPath id; +}; + /// Information about the Accelerator system. class SysInfo : public Service { public: @@ -70,4 +106,4 @@ class MMIOSysInfo final : public SysInfo { } // namespace services } // namespace esi -#endif // ESI_RUNTIME_STDSERVICES_H +#endif // ESI_RUNTIME_SERVICES_H diff --git a/lib/Dialect/ESI/runtime/cpp/include/esi/backends/Cosim.h b/lib/Dialect/ESI/runtime/cpp/include/esi/backends/Cosim.h index f852ebfaf064..d20b204b088f 100644 --- a/lib/Dialect/ESI/runtime/cpp/include/esi/backends/Cosim.h +++ b/lib/Dialect/ESI/runtime/cpp/include/esi/backends/Cosim.h @@ -29,12 +29,13 @@ namespace backends { namespace cosim { /// Connect to an ESI simulation. -class CosimAccelerator : public esi::Accelerator { +class CosimAccelerator : public esi::AcceleratorConnection { public: struct Impl; CosimAccelerator(std::string hostname, uint16_t port); - static std::unique_ptr connect(std::string connectionString); + static std::unique_ptr + connect(std::string connectionString); protected: virtual Service *createService(Service::Type service, AppIDPath path, diff --git a/lib/Dialect/ESI/runtime/cpp/include/esi/backends/Trace.h b/lib/Dialect/ESI/runtime/cpp/include/esi/backends/Trace.h index 8965cd2e88dc..918fba8c05be 100644 --- a/lib/Dialect/ESI/runtime/cpp/include/esi/backends/Trace.h +++ b/lib/Dialect/ESI/runtime/cpp/include/esi/backends/Trace.h @@ -32,7 +32,7 @@ namespace backends { namespace trace { /// Connect to an ESI simulation. -class TraceAccelerator : public esi::Accelerator { +class TraceAccelerator : public esi::AcceleratorConnection { public: enum Mode { // Write data sent to the accelerator to the trace file. Produce random @@ -55,7 +55,8 @@ class TraceAccelerator : public esi::Accelerator { /// Parse the connection string and instantiate the accelerator. Format is: /// ":[:]". - static std::unique_ptr connect(std::string connectionString); + static std::unique_ptr + connect(std::string connectionString); /// Internal implementation. struct Impl; diff --git a/lib/Dialect/ESI/runtime/cpp/lib/Accelerator.cpp b/lib/Dialect/ESI/runtime/cpp/lib/Accelerator.cpp index 0589c9f65f05..d32c2c26916a 100644 --- a/lib/Dialect/ESI/runtime/cpp/lib/Accelerator.cpp +++ b/lib/Dialect/ESI/runtime/cpp/lib/Accelerator.cpp @@ -22,22 +22,39 @@ using namespace std; using namespace esi; using namespace esi::services; -CustomService::CustomService(AppIDPath idPath, - const ServiceImplDetails &details, - const HWClientDetails &clients) - : id(idPath) { - if (auto f = details.find("service"); f != details.end()) { - serviceSymbol = any_cast(f->second); - // Strip off initial '@'. - serviceSymbol = serviceSymbol.substr(1); - } +namespace esi { + +/// Build an index of children by AppID. +static map +buildIndex(const vector> &insts) { + map index; + for (auto &item : insts) + index[item->getID()] = item.get(); + return index; } -namespace esi { -services::Service *Accelerator::getService(Service::Type svcType, AppIDPath id, - std::string implName, - ServiceImplDetails details, - HWClientDetails clients) { +/// Build an index of ports by AppID. +static map +buildIndex(const vector &ports) { + map index; + for (auto &item : ports) + index.emplace(item.getID(), item); + return index; +} + +HWModule::HWModule(std::optional info, + std::vector> children, + std::vector services, + std::vector ports) + : info(info), children(std::move(children)), + childIndex(buildIndex(this->children)), services(services), ports(ports), + portIndex(buildIndex(this->ports)) {} + +services::Service *AcceleratorConnection::getService(Service::Type svcType, + AppIDPath id, + std::string implName, + ServiceImplDetails details, + HWClientDetails clients) { unique_ptr &cacheEntry = serviceCache[make_tuple(&svcType, id)]; if (cacheEntry == nullptr) { Service *svc = createService(svcType, id, implName, details, clients); @@ -59,7 +76,7 @@ void registerBackend(string name, BackendCreate create) { } } // namespace internal -unique_ptr connect(string backend, string connection) { +unique_ptr connect(string backend, string connection) { auto f = internal::backendRegistry.find(backend); if (f == internal::backendRegistry.end()) throw runtime_error("Backend not found"); diff --git a/lib/Dialect/ESI/runtime/cpp/lib/Manifest.cpp b/lib/Dialect/ESI/runtime/cpp/lib/Manifest.cpp index 6018490d4261..111c9baacfdf 100644 --- a/lib/Dialect/ESI/runtime/cpp/lib/Manifest.cpp +++ b/lib/Dialect/ESI/runtime/cpp/lib/Manifest.cpp @@ -14,8 +14,7 @@ #include "esi/Manifest.h" #include "esi/Accelerator.h" -#include "esi/Design.h" -#include "esi/StdServices.h" +#include "esi/Services.h" #include @@ -50,13 +49,14 @@ class Manifest::Impl { /// Get a Service for the service specified in 'json'. Update the /// activeServices table. - services::Service *getService(AppIDPath idPath, Accelerator &, + services::Service *getService(AppIDPath idPath, AcceleratorConnection &, const nlohmann::json &, ServiceTable &activeServices) const; /// Get all the services in the description of an instance. Update the active /// services table. - vector getServices(AppIDPath idPath, Accelerator &, + vector getServices(AppIDPath idPath, + AcceleratorConnection &, const nlohmann::json &, ServiceTable &activeServices) const; @@ -69,13 +69,14 @@ class Manifest::Impl { /// Build the set of child instances (recursively) for the module instance /// description. vector> - getChildInstances(AppIDPath idPath, Accelerator &acc, + getChildInstances(AppIDPath idPath, AcceleratorConnection &acc, const ServiceTable &activeServices, const nlohmann::json &instJson) const; /// Get a single child instance. Implicitly copy the active services table so /// that it can be safely updated for the child's branch of the tree. - unique_ptr getChildInstance(AppIDPath idPath, Accelerator &acc, + unique_ptr getChildInstance(AppIDPath idPath, + AcceleratorConnection &acc, ServiceTable activeServices, const nlohmann::json &childJson) const; @@ -96,7 +97,8 @@ class Manifest::Impl { /// Build a dynamic API for the Accelerator connection 'acc' based on the /// manifest stored herein. - unique_ptr buildDesign(Accelerator &acc) const; + unique_ptr buildAccelerator(AcceleratorConnection &acc, + std::shared_ptr me) const; const Type &parseType(const nlohmann::json &typeJson); @@ -202,7 +204,9 @@ Manifest::Impl::Impl(const string &manifestStr) { populateTypes(manifestJson.at("types")); } -unique_ptr Manifest::Impl::buildDesign(Accelerator &acc) const { +unique_ptr +Manifest::Impl::buildAccelerator(AcceleratorConnection &acc, + std::shared_ptr me) const { auto designJson = manifestJson.at("design"); // Get the initial active services table. Update it as we descend down. @@ -210,10 +214,10 @@ unique_ptr Manifest::Impl::buildDesign(Accelerator &acc) const { vector services = getServices({}, acc, designJson, activeSvcs); - return make_unique(getModInfo(designJson), - getChildInstances({}, acc, activeSvcs, designJson), - services, - getBundlePorts({}, activeSvcs, designJson)); + return make_unique( + getModInfo(designJson), + getChildInstances({}, acc, activeSvcs, designJson), services, + getBundlePorts({}, activeSvcs, designJson), me); } optional @@ -228,7 +232,7 @@ Manifest::Impl::getModInfo(const nlohmann::json &json) const { } vector> -Manifest::Impl::getChildInstances(AppIDPath idPath, Accelerator &acc, +Manifest::Impl::getChildInstances(AppIDPath idPath, AcceleratorConnection &acc, const ServiceTable &activeServices, const nlohmann::json &instJson) const { vector> ret; @@ -240,7 +244,7 @@ Manifest::Impl::getChildInstances(AppIDPath idPath, Accelerator &acc, return ret; } unique_ptr -Manifest::Impl::getChildInstance(AppIDPath idPath, Accelerator &acc, +Manifest::Impl::getChildInstance(AppIDPath idPath, AcceleratorConnection &acc, ServiceTable activeServices, const nlohmann::json &child) const { AppID childID = parseID(child.at("app_id")); @@ -256,7 +260,7 @@ Manifest::Impl::getChildInstance(AppIDPath idPath, Accelerator &acc, } services::Service * -Manifest::Impl::getService(AppIDPath idPath, Accelerator &acc, +Manifest::Impl::getService(AppIDPath idPath, AcceleratorConnection &acc, const nlohmann::json &svcJson, ServiceTable &activeServices) const { @@ -304,7 +308,7 @@ Manifest::Impl::getService(AppIDPath idPath, Accelerator &acc, } vector -Manifest::Impl::getServices(AppIDPath idPath, Accelerator &acc, +Manifest::Impl::getServices(AppIDPath idPath, AcceleratorConnection &acc, const nlohmann::json &svcsJson, ServiceTable &activeServices) const { vector ret; @@ -503,33 +507,33 @@ void Manifest::Impl::populateTypes(const nlohmann::json &typesJson) { // Manifest class implementation. //===----------------------------------------------------------------------===// -Manifest::Manifest(const string &jsonManifest) - : impl(*new Impl(jsonManifest)) {} +Manifest::Manifest(const string &jsonManifest) : impl(new Impl(jsonManifest)) {} Manifest::~Manifest() { delete &impl; } uint32_t Manifest::getApiVersion() const { - return impl.at("api_version").get(); + return impl->at("api_version").get(); } vector Manifest::getModuleInfos() const { vector ret; - for (auto &mod : impl.at("symbols")) + for (auto &mod : impl->at("symbols")) ret.push_back(parseModuleInfo(mod)); return ret; } -unique_ptr Manifest::buildDesign(Accelerator &acc) const { - return impl.buildDesign(acc); +unique_ptr +Manifest::buildAccelerator(AcceleratorConnection &acc) const { + return impl->buildAccelerator(acc, impl); } optional> Manifest::getType(Type::ID id) const { - if (auto f = impl._types.find(id); f != impl._types.end()) + if (auto f = impl->_types.find(id); f != impl->_types.end()) return *f->second; return nullopt; } const vector> &Manifest::getTypeTable() const { - return impl.getTypeTable(); + return impl->getTypeTable(); } //===----------------------------------------------------------------------===// diff --git a/lib/Dialect/ESI/runtime/cpp/lib/Design.cpp b/lib/Dialect/ESI/runtime/cpp/lib/Ports.cpp similarity index 58% rename from lib/Dialect/ESI/runtime/cpp/lib/Design.cpp rename to lib/Dialect/ESI/runtime/cpp/lib/Ports.cpp index 9d537325cb04..44f79fadd778 100644 --- a/lib/Dialect/ESI/runtime/cpp/lib/Design.cpp +++ b/lib/Dialect/ESI/runtime/cpp/lib/Ports.cpp @@ -1,4 +1,4 @@ -//===- Design.cpp - Implementation of dynamic API -------------------------===// +//===- Ports.cpp - ESI communication channels -------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -12,8 +12,7 @@ // //===----------------------------------------------------------------------===// -#include "esi/Design.h" -#include "esi/Accelerator.h" +#include "esi/Ports.h" using namespace std; using namespace esi; @@ -40,29 +39,3 @@ ReadChannelPort &BundlePort::getRawRead(const string &name) const { throw runtime_error("Channel '" + name + "' is not a read channel"); return *read; } - -/// Build an index of children by AppID. -static map -buildIndex(const vector> &insts) { - map index; - for (auto &item : insts) - index[item->getID()] = item.get(); - return index; -} - -/// Build an index of ports by AppID. -static map -buildIndex(const vector &ports) { - map index; - for (auto &item : ports) - index.emplace(item.getID(), item); - return index; -} - -Design::Design(std::optional info, - std::vector> children, - std::vector services, - std::vector ports) - : info(info), children(std::move(children)), - childIndex(buildIndex(this->children)), services(services), ports(ports), - portIndex(buildIndex(this->ports)) {} diff --git a/lib/Dialect/ESI/runtime/cpp/lib/StdServices.cpp b/lib/Dialect/ESI/runtime/cpp/lib/Services.cpp similarity index 82% rename from lib/Dialect/ESI/runtime/cpp/lib/StdServices.cpp rename to lib/Dialect/ESI/runtime/cpp/lib/Services.cpp index 3c741431ca03..d56b2581d20a 100644 --- a/lib/Dialect/ESI/runtime/cpp/lib/StdServices.cpp +++ b/lib/Dialect/ESI/runtime/cpp/lib/Services.cpp @@ -13,7 +13,8 @@ // //===----------------------------------------------------------------------===// -#include "esi/StdServices.h" +#include "esi/Services.h" +#include "esi/Accelerator.h" #include "zlib.h" @@ -57,3 +58,14 @@ vector MMIOSysInfo::getCompressedManifest() const { assert(false && "Not implemented"); throw runtime_error("Not implemented"); } + +CustomService::CustomService(AppIDPath idPath, + const ServiceImplDetails &details, + const HWClientDetails &clients) + : id(idPath) { + if (auto f = details.find("service"); f != details.end()) { + serviceSymbol = any_cast(f->second); + // Strip off initial '@'. + serviceSymbol = serviceSymbol.substr(1); + } +} diff --git a/lib/Dialect/ESI/runtime/cpp/lib/backends/Cosim.cpp b/lib/Dialect/ESI/runtime/cpp/lib/backends/Cosim.cpp index 9e669a8d1d33..70d876c96a87 100644 --- a/lib/Dialect/ESI/runtime/cpp/lib/backends/Cosim.cpp +++ b/lib/Dialect/ESI/runtime/cpp/lib/backends/Cosim.cpp @@ -14,7 +14,7 @@ //===----------------------------------------------------------------------===// #include "esi/backends/Cosim.h" -#include "esi/StdServices.h" +#include "esi/Services.h" #include "CosimDpi.capnp.h" #include @@ -33,7 +33,8 @@ using namespace esi::backends::cosim; /// traditional 'host:port' syntax and a path to 'cosim.cfg' which is output by /// the cosimulation when it starts (which is useful when it chooses its own /// port). -unique_ptr CosimAccelerator::connect(string connectionString) { +unique_ptr +CosimAccelerator::connect(string connectionString) { string portStr; string host = "localhost"; diff --git a/lib/Dialect/ESI/runtime/cpp/lib/backends/Trace.cpp b/lib/Dialect/ESI/runtime/cpp/lib/backends/Trace.cpp index b7e7ed891c36..f037b8b84c0e 100644 --- a/lib/Dialect/ESI/runtime/cpp/lib/backends/Trace.cpp +++ b/lib/Dialect/ESI/runtime/cpp/lib/backends/Trace.cpp @@ -13,8 +13,9 @@ //===----------------------------------------------------------------------===// #include "esi/backends/Trace.h" -#include "esi/Design.h" -#include "esi/StdServices.h" + +#include "esi/Accelerator.h" +#include "esi/Services.h" #include "esi/Utils.h" #include @@ -83,7 +84,8 @@ void TraceAccelerator::Impl::write(const AppIDPath &id, const string &portName, *traceWrite << "write " << id << '.' << portName << ": " << b64data << endl; } -unique_ptr TraceAccelerator::connect(string connectionString) { +unique_ptr +TraceAccelerator::connect(string connectionString) { string modeStr; string manifestPath; string traceFile = "trace.json"; diff --git a/lib/Dialect/ESI/runtime/cpp/tools/esiquery.cpp b/lib/Dialect/ESI/runtime/cpp/tools/esiquery.cpp index f7ea7c0b1a78..d29ac033fb9d 100644 --- a/lib/Dialect/ESI/runtime/cpp/tools/esiquery.cpp +++ b/lib/Dialect/ESI/runtime/cpp/tools/esiquery.cpp @@ -15,7 +15,7 @@ #include "esi/Accelerator.h" #include "esi/Manifest.h" -#include "esi/StdServices.h" +#include "esi/Services.h" #include #include @@ -25,7 +25,7 @@ using namespace std; using namespace esi; -void printInfo(ostream &os, Accelerator &acc); +void printInfo(ostream &os, AcceleratorConnection &acc); int main(int argc, const char *argv[]) { // TODO: find a command line parser library rather than doing this by hand. @@ -42,7 +42,7 @@ int main(int argc, const char *argv[]) { cmd = argv[3]; try { - unique_ptr acc = registry::connect(backend, conn); + unique_ptr acc = registry::connect(backend, conn); const auto &info = *acc->getService(); if (cmd == "version") @@ -66,7 +66,7 @@ int main(int argc, const char *argv[]) { } } -void printInfo(ostream &os, Accelerator &acc) { +void printInfo(ostream &os, AcceleratorConnection &acc) { string jsonManifest = acc.getService()->getJsonManifest(); Manifest m(jsonManifest); os << "API version: " << m.getApiVersion() << endl << endl; diff --git a/lib/Dialect/ESI/runtime/python/esi/__init__.py b/lib/Dialect/ESI/runtime/python/esi/__init__.py index 51f51c517b64..af06e501ef17 100644 --- a/lib/Dialect/ESI/runtime/python/esi/__init__.py +++ b/lib/Dialect/ESI/runtime/python/esi/__init__.py @@ -2,12 +2,12 @@ # See https://llvm.org/LICENSE.txt for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -from .accelerator import Accelerator +from .accelerator import AcceleratorConnection from .esiCppAccel import (AppID, Type, BundleType, ChannelType, ArrayType, StructType, BitsType, UIntType, SIntType) __all__ = [ - "Accelerator", "AppID", "Type", "BundleType", "ChannelType", "ArrayType", - "StructType", "BitsType", "UIntType", "SIntType" + "AcceleratorConnection", "AppID", "Type", "BundleType", "ChannelType", + "ArrayType", "StructType", "BitsType", "UIntType", "SIntType" ] diff --git a/lib/Dialect/ESI/runtime/python/esi/accelerator.py b/lib/Dialect/ESI/runtime/python/esi/accelerator.py index e40efd6d5c08..968152400f32 100644 --- a/lib/Dialect/ESI/runtime/python/esi/accelerator.py +++ b/lib/Dialect/ESI/runtime/python/esi/accelerator.py @@ -5,9 +5,9 @@ from . import esiCppAccel as cpp -class Accelerator(cpp.Accelerator): - """A connection to an ESI accelerator.""" +class AcceleratorConnection(cpp.AcceleratorConnection): + """An ESI accelerator.""" def manifest(self) -> cpp.Manifest: - """Get and parse the accelerator manifest.""" + """Returns the accelerator's manifest.""" return cpp.Manifest(self.sysinfo().json_manifest()) diff --git a/lib/Dialect/ESI/runtime/python/esi/esiCppAccel.cpp b/lib/Dialect/ESI/runtime/python/esi/esiCppAccel.cpp index 1dee56d6763f..267508129813 100644 --- a/lib/Dialect/ESI/runtime/python/esi/esiCppAccel.cpp +++ b/lib/Dialect/ESI/runtime/python/esi/esiCppAccel.cpp @@ -11,8 +11,7 @@ //===----------------------------------------------------------------------===// #include "esi/Accelerator.h" -#include "esi/Design.h" -#include "esi/StdServices.h" +#include "esi/Services.h" #include @@ -153,37 +152,41 @@ PYBIND11_MODULE(esiCppAccel, m) { // Store this variable (not commonly done) as the "children" method needs for // "Instance" to be defined first. - auto design = - py::class_(m, "Design") - .def_property_readonly("info", &Design::getInfo) - .def_property_readonly("ports", &Design::getPorts, + auto hwmodule = + py::class_(m, "HWModule") + .def_property_readonly("info", &HWModule::getInfo) + .def_property_readonly("ports", &HWModule::getPorts, py::return_value_policy::reference_internal); // In order to inherit methods from "Design", it needs to be defined first. - py::class_(m, "Instance") + py::class_(m, "Instance") .def_property_readonly("id", &Instance::getID); + py::class_(m, "Accelerator"); + // Since this returns a vector of Instance*, we need to define Instance first // or else pybind11-stubgen complains. - design.def_property_readonly("children", &Design::getChildren, - py::return_value_policy::reference_internal); + hwmodule.def_property_readonly("children", &HWModule::getChildren, + py::return_value_policy::reference_internal); - py::class_(m, "Accelerator") + py::class_(m, "AcceleratorConnection") .def(py::init(®istry::connect)) .def( "sysinfo", - [](Accelerator &acc) { + [](AcceleratorConnection &acc) { return acc.getService({}); }, py::return_value_policy::reference_internal) .def( "get_service_mmio", - [](Accelerator &acc) { return acc.getService({}); }, + [](AcceleratorConnection &acc) { + return acc.getService({}); + }, py::return_value_policy::reference_internal); py::class_(m, "Manifest") .def(py::init()) .def_property_readonly("api_version", &Manifest::getApiVersion) - .def("build_design", &Manifest::buildDesign) + .def("build_accelerator", &Manifest::buildAccelerator) .def_property_readonly("type_table", &Manifest::getTypeTable); } diff --git a/lib/Dialect/ESI/runtime/python/esi/esiCppAccel.pyi b/lib/Dialect/ESI/runtime/python/esi/esiCppAccel.pyi index 9e046472e476..71d85c62bafa 100644 --- a/lib/Dialect/ESI/runtime/python/esi/esiCppAccel.pyi +++ b/lib/Dialect/ESI/runtime/python/esi/esiCppAccel.pyi @@ -7,15 +7,19 @@ from __future__ import annotations import typing __all__ = [ - 'Accelerator', 'AnyType', 'AppID', 'ArrayType', 'BitVectorType', 'BitsType', - 'BundlePort', 'BundleType', 'ChannelPort', 'ChannelType', 'Design', - 'Direction', 'From', 'Instance', 'IntegerType', 'MMIO', 'Manifest', - 'ModuleInfo', 'ReadChannelPort', 'SIntType', 'StructType', 'SysInfo', 'To', - 'Type', 'UIntType', 'WriteChannelPort' + 'Accelerator', 'AcceleratorConnection', 'AnyType', 'AppID', 'ArrayType', + 'BitVectorType', 'BitsType', 'BundlePort', 'BundleType', 'ChannelPort', + 'ChannelType', 'Direction', 'From', 'HWModule', 'Instance', 'IntegerType', + 'MMIO', 'Manifest', 'ModuleInfo', 'ReadChannelPort', 'SIntType', + 'StructType', 'SysInfo', 'To', 'Type', 'UIntType', 'WriteChannelPort' ] -class Accelerator: +class Accelerator(HWModule): + pass + + +class AcceleratorConnection: def __init__(self, arg0: str, arg1: str) -> None: ... @@ -117,21 +121,6 @@ class ChannelType(Type): ... -class Design: - - @property - def children(self) -> dict[AppID, Instance]: - ... - - @property - def info(self) -> ModuleInfo | None: - ... - - @property - def ports(self) -> dict[AppID, BundlePort]: - ... - - class Direction: """ Members: @@ -185,7 +174,22 @@ class Direction: ... -class Instance(Design): +class HWModule: + + @property + def children(self) -> dict[AppID, Instance]: + ... + + @property + def info(self) -> ModuleInfo | None: + ... + + @property + def ports(self) -> dict[AppID, BundlePort]: + ... + + +class Instance(HWModule): @property def id(self) -> AppID: @@ -210,7 +214,7 @@ class Manifest: def __init__(self, arg0: str) -> None: ... - def build_design(self, arg0: Accelerator) -> Design: + def build_accelerator(self, arg0: AcceleratorConnection) -> Accelerator: ... @property