CIRCT  19.0.0git
esiCppAccel.cpp
Go to the documentation of this file.
1 //===- esiaccel.cpp - ESI runtime python bindings ---------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // Simply wrap the C++ API into a Python module called 'esiaccel'.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "esi/Accelerator.h"
14 #include "esi/Services.h"
15 
16 #include "esi/backends/Cosim.h"
17 
18 #include <sstream>
19 
20 // pybind11 includes
21 #include <pybind11/pybind11.h>
22 namespace py = pybind11;
23 
24 #include <pybind11/stl.h>
25 
26 using namespace esi;
27 using namespace esi::services;
28 
29 namespace pybind11 {
30 /// Pybind11 needs a little help downcasting with non-bound instances.
31 template <>
32 struct polymorphic_type_hook<ChannelPort> {
33  static const void *get(const ChannelPort *port, const std::type_info *&type) {
34  if (auto p = dynamic_cast<const WriteChannelPort *>(port)) {
35  type = &typeid(WriteChannelPort);
36  return p;
37  }
38  if (auto p = dynamic_cast<const ReadChannelPort *>(port)) {
39  type = &typeid(ReadChannelPort);
40  return p;
41  }
42  return port;
43  }
44 };
45 } // namespace pybind11
46 
47 // NOLINTNEXTLINE(readability-identifier-naming)
48 PYBIND11_MODULE(esiCppAccel, m) {
49  py::class_<Type>(m, "Type")
50  .def_property_readonly("id", &Type::getID)
51  .def("__repr__", [](Type &t) { return "<" + t.getID() + ">"; });
52  py::class_<ChannelType, Type>(m, "ChannelType")
53  .def_property_readonly("inner", &ChannelType::getInner,
54  py::return_value_policy::reference);
55  py::enum_<BundleType::Direction>(m, "Direction")
56  .value("To", BundleType::Direction::To)
57  .value("From", BundleType::Direction::From)
58  .export_values();
59  py::class_<BundleType, Type>(m, "BundleType")
60  .def_property_readonly("channels", &BundleType::getChannels,
61  py::return_value_policy::reference);
62  py::class_<VoidType, Type>(m, "VoidType");
63  py::class_<AnyType, Type>(m, "AnyType");
64  py::class_<BitVectorType, Type>(m, "BitVectorType")
65  .def_property_readonly("width", &BitVectorType::getWidth);
66  py::class_<BitsType, BitVectorType>(m, "BitsType");
67  py::class_<IntegerType, BitVectorType>(m, "IntegerType");
68  py::class_<SIntType, IntegerType>(m, "SIntType");
69  py::class_<UIntType, IntegerType>(m, "UIntType");
70  py::class_<StructType, Type>(m, "StructType")
71  .def_property_readonly("fields", &StructType::getFields,
72  py::return_value_policy::reference);
73  py::class_<ArrayType, Type>(m, "ArrayType")
74  .def_property_readonly("element", &ArrayType::getElementType,
75  py::return_value_policy::reference)
76  .def_property_readonly("size", &ArrayType::getSize);
77 
78  py::class_<Context>(m, "Context")
79  .def(py::init<>())
80  .def("connect", &Context::connect);
81 
82  py::class_<ModuleInfo>(m, "ModuleInfo")
83  .def_property_readonly("name", [](ModuleInfo &info) { return info.name; })
84  .def_property_readonly("summary",
85  [](ModuleInfo &info) { return info.summary; })
86  .def_property_readonly("version",
87  [](ModuleInfo &info) { return info.version; })
88  .def_property_readonly("repo", [](ModuleInfo &info) { return info.repo; })
89  .def_property_readonly("commit_hash",
90  [](ModuleInfo &info) { return info.commitHash; })
91  // TODO: "extra" field.
92  .def("__repr__", [](ModuleInfo &info) {
93  std::string ret;
94  std::stringstream os(ret);
95  os << info;
96  return os.str();
97  });
98 
99  py::class_<SysInfo>(m, "SysInfo")
100  .def("esi_version", &SysInfo::getEsiVersion)
101  .def("json_manifest", &SysInfo::getJsonManifest);
102 
103  py::class_<services::MMIO>(m, "MMIO")
104  .def("read", &services::MMIO::read)
105  .def("write", &services::MMIO::write);
106 
107  py::class_<AppID>(m, "AppID")
108  .def(py::init<std::string, std::optional<uint32_t>>(), py::arg("name"),
109  py::arg("idx") = std::nullopt)
110  .def_property_readonly("name", [](AppID &id) { return id.name; })
111  .def_property_readonly("idx",
112  [](AppID &id) -> py::object {
113  if (id.idx)
114  return py::cast(id.idx);
115  return py::none();
116  })
117  .def("__repr__",
118  [](AppID &id) {
119  std::string ret = "<" + id.name;
120  if (id.idx)
121  ret = ret + "[" + std::to_string(*id.idx) + "]";
122  ret = ret + ">";
123  return ret;
124  })
125  .def("__eq__", [](AppID &a, AppID &b) { return a == b; })
126  .def("__hash__", [](AppID &id) {
127  // TODO: This is a bad hash function. Replace it.
128  return std::hash<std::string>{}(id.name) ^
129  (std::hash<uint32_t>{}(id.idx.value_or(-1)) << 1);
130  });
131 
132  py::class_<std::future<MessageData>>(m, "MessageDataFuture")
133  .def("valid", &std::future<MessageData>::valid)
134  .def("wait", &std::future<MessageData>::wait)
135  .def("get", [](std::future<MessageData> &f) {
136  MessageData data = f.get();
137  return py::bytearray((const char *)data.getBytes(), data.getSize());
138  });
139 
140  py::class_<ChannelPort>(m, "ChannelPort")
141  .def("connect", &ChannelPort::connect)
142  .def_property_readonly("type", &ChannelPort::getType,
143  py::return_value_policy::reference);
144 
145  py::class_<WriteChannelPort, ChannelPort>(m, "WriteChannelPort")
146  .def("write", [](WriteChannelPort &p, py::bytearray &data) {
147  py::buffer_info info(py::buffer(data).request());
148  std::vector<uint8_t> dataVec((uint8_t *)info.ptr,
149  (uint8_t *)info.ptr + info.size);
150  p.write(dataVec);
151  });
152  py::class_<ReadChannelPort, ChannelPort>(m, "ReadChannelPort")
153  .def("read",
154  [](ReadChannelPort &p) -> py::object {
155  MessageData data;
156  if (!p.read(data))
157  return py::none();
158  return py::bytearray((const char *)data.getBytes(),
159  data.getSize());
160  })
161  .def("read_async", &ReadChannelPort::readAsync);
162 
163  py::class_<BundlePort>(m, "BundlePort")
164  .def_property_readonly("id", &BundlePort::getID)
165  .def_property_readonly("channels", &BundlePort::getChannels,
166  py::return_value_policy::reference)
167  .def("getWrite", &BundlePort::getRawWrite,
168  py::return_value_policy::reference)
169  .def("getRead", &BundlePort::getRawRead,
170  py::return_value_policy::reference);
171 
172  py::class_<ServicePort, BundlePort>(m, "ServicePort");
173  py::class_<FuncService::Function, ServicePort>(m, "Function")
174  .def(
175  "call",
176  [](FuncService::Function &self,
177  py::bytearray msg) -> std::future<MessageData> {
178  py::buffer_info info(py::buffer(msg).request());
179  std::vector<uint8_t> dataVec((uint8_t *)info.ptr,
180  (uint8_t *)info.ptr + info.size);
181  MessageData data(dataVec);
182  return self.call(data);
183  },
184  py::return_value_policy::take_ownership)
185  .def("connect", &FuncService::Function::connect);
186 
187  // Store this variable (not commonly done) as the "children" method needs for
188  // "Instance" to be defined first.
189  auto hwmodule =
190  py::class_<HWModule>(m, "HWModule")
191  .def_property_readonly("info", &HWModule::getInfo)
192  .def_property_readonly("ports", &HWModule::getPorts,
193  py::return_value_policy::reference);
194 
195  // In order to inherit methods from "HWModule", it needs to be defined first.
196  py::class_<Instance, HWModule>(m, "Instance")
197  .def_property_readonly("id", &Instance::getID);
198 
199  py::class_<Accelerator, HWModule>(m, "Accelerator");
200 
201  // Since this returns a vector of Instance*, we need to define Instance first
202  // or else pybind11-stubgen complains.
203  hwmodule.def_property_readonly("children", &HWModule::getChildren,
204  py::return_value_policy::reference);
205 
206  auto accConn = py::class_<AcceleratorConnection>(m, "AcceleratorConnection")
207  .def(py::init(&registry::connect))
208  .def(
209  "sysinfo",
210  [](AcceleratorConnection &acc) {
211  return acc.getService<services::SysInfo>({});
212  },
213  py::return_value_policy::reference)
214  .def(
215  "get_service_mmio",
216  [](AcceleratorConnection &acc) {
217  return acc.getService<services::MMIO>({});
218  },
219  py::return_value_policy::reference);
220 
221 // Only include this cosim-only feature if cosim is enabled.It's a bit of a hack
222 // to test both styles of manifest retrieval for cosim. Come up with a more
223 // generic way to set accelerator-specific properties/configurations.
224 #ifdef ESI_COSIM
225  py::enum_<backends::cosim::CosimAccelerator::ManifestMethod>(
226  m, "CosimManifestMethod")
227  .value("ManifestCosim",
228  backends::cosim::CosimAccelerator::ManifestMethod::Cosim)
229  .value("ManifestMMIO",
230  backends::cosim::CosimAccelerator::ManifestMethod::MMIO)
231  .export_values();
232 
233  accConn.def(
234  "set_manifest_method",
235  [](AcceleratorConnection &acc,
236  backends::cosim::CosimAccelerator::ManifestMethod method) {
237  auto cosim = dynamic_cast<backends::cosim::CosimAccelerator *>(&acc);
238  if (!cosim)
239  throw std::runtime_error(
240  "set_manifest_method only supported for cosim connections");
241  cosim->setManifestMethod(method);
242  });
243 #endif // ESI_COSIM
244 
245  py::class_<Manifest>(m, "Manifest")
246  .def(py::init<Context &, std::string>())
247  .def_property_readonly("api_version", &Manifest::getApiVersion)
248  .def("build_accelerator", &Manifest::buildAccelerator,
249  py::return_value_policy::take_ownership)
250  .def_property_readonly("type_table", &Manifest::getTypeTable)
251  .def_property_readonly("module_infos", &Manifest::getModuleInfos);
252 }
Abstract class representing a connection to an accelerator.
Definition: Accelerator.h:75
ServiceClass * getService(AppIDPath id={}, std::string implName={}, ServiceImplDetails details={}, HWClientDetails clients={})
Get a typed reference to a particular service type.
Definition: Accelerator.h:92
const Type * getElementType() const
Definition: Types.h:159
uint64_t getSize() const
Definition: Types.h:160
uint64_t getWidth() const
Definition: Types.h:96
ReadChannelPort & getRawRead(const std::string &name) const
Definition: Ports.cpp:36
WriteChannelPort & getRawWrite(const std::string &name) const
Get access to the raw byte streams of a channel.
Definition: Ports.cpp:26
const std::map< std::string, ChannelPort & > & getChannels() const
Definition: Ports.h:95
AppID getID() const
Get the ID of the port.
Definition: Ports.h:87
const ChannelVector & getChannels() const
Definition: Types.h:54
Unidirectional channels are the basic communication primitive between the host and accelerator.
Definition: Ports.h:31
virtual void connect()
Definition: Ports.h:36
const Type * getType() const
Definition: Ports.h:39
const Type * getInner() const
Definition: Types.h:66
std::unique_ptr< AcceleratorConnection > connect(std::string backend, std::string connection)
Connect to an accelerator backend.
Definition: Context.cpp:27
const std::map< AppID, Instance * > & getChildren() const
Access the module's children by ID.
Definition: Design.h:67
std::optional< ModuleInfo > getInfo() const
Access the module's metadata, if any.
Definition: Design.h:58
const std::map< AppID, const BundlePort & > & getPorts() const
Access the module's ports by ID.
Definition: Design.h:76
const AppID getID() const
Get the instance's ID, which it will always have.
Definition: Design.h:103
const std::vector< const Type * > & getTypeTable() const
The Type Table is an ordered list of types.
Definition: Manifest.cpp:540
std::unique_ptr< Accelerator > buildAccelerator(AcceleratorConnection &acc) const
Definition: Manifest.cpp:536
uint32_t getApiVersion() const
Definition: Manifest.cpp:524
std::vector< ModuleInfo > getModuleInfos() const
Definition: Manifest.cpp:528
A logical chunk of data representing serialized data.
Definition: Common.h:85
A ChannelPort which reads data from the accelerator.
Definition: Ports.h:55
virtual std::future< MessageData > readAsync()
Asynchronous read.
Definition: Ports.cpp:46
const FieldVector & getFields() const
Definition: Types.h:137
Root class of the ESI type system.
Definition: Types.h:27
ID getID() const
Definition: Types.h:33
A ChannelPort which sends data to the accelerator.
Definition: Ports.h:46
virtual void write(const MessageData &)=0
A very basic write API. Will likely change for performance reasons.
A function call which gets attached to a service port.
Definition: Services.h:128
virtual void write(uint32_t addr, uint32_t data)=0
virtual uint32_t read(uint32_t addr) const =0
Information about the Accelerator system.
Definition: Services.h:77
virtual std::string getJsonManifest() const
Return the JSON-formatted system manifest.
Definition: Services.cpp:34
virtual uint32_t getEsiVersion() const =0
Get the ESI version number to check version compatibility.
PYBIND11_MODULE(esiCppAccel, m)
Definition: esiCppAccel.cpp:48
std::unique_ptr< AcceleratorConnection > connect(Context &ctxt, std::string backend, std::string connection)
Definition: esi.py:1
const std::optional< std::string > commitHash
Definition: Common.h:61
const std::optional< std::string > repo
Definition: Common.h:60
const std::optional< std::string > version
Definition: Common.h:59
const std::optional< std::string > summary
Definition: Common.h:58
const std::optional< std::string > name
Definition: Common.h:57
static const void * get(const ChannelPort *port, const std::type_info *&type)
Definition: esiCppAccel.cpp:33