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_<ModuleInfo>(m, "ModuleInfo")
79  .def_property_readonly("name", [](ModuleInfo &info) { return info.name; })
80  .def_property_readonly("summary",
81  [](ModuleInfo &info) { return info.summary; })
82  .def_property_readonly("version",
83  [](ModuleInfo &info) { return info.version; })
84  .def_property_readonly("repo", [](ModuleInfo &info) { return info.repo; })
85  .def_property_readonly("commit_hash",
86  [](ModuleInfo &info) { return info.commitHash; })
87  // TODO: "extra" field.
88  .def("__repr__", [](ModuleInfo &info) {
89  std::string ret;
90  std::stringstream os(ret);
91  os << info;
92  return os.str();
93  });
94 
95  py::class_<SysInfo>(m, "SysInfo")
96  .def("esi_version", &SysInfo::getEsiVersion)
97  .def("json_manifest", &SysInfo::getJsonManifest);
98 
99  py::class_<services::MMIO>(m, "MMIO")
100  .def("read", &services::MMIO::read)
101  .def("write", &services::MMIO::write);
102 
103  py::class_<services::HostMem::HostMemRegion>(m, "HostMemRegion")
104  .def_property_readonly("ptr",
106  return reinterpret_cast<uintptr_t>(mem.getPtr());
107  })
108  .def_property_readonly("size",
110 
111  py::class_<services::HostMem::Options>(m, "HostMemOptions")
112  .def(py::init<>())
113  .def_readwrite("writeable", &services::HostMem::Options::writeable)
114  .def_readwrite("use_large_pages",
116  .def("__repr__", [](services::HostMem::Options &opts) {
117  std::string ret = "HostMemOptions(";
118  if (opts.writeable)
119  ret += "writeable ";
120  if (opts.useLargePages)
121  ret += "use_large_pages";
122  ret += ")";
123  return ret;
124  });
125 
126  py::class_<services::HostMem>(m, "HostMem")
127  .def("allocate", &services::HostMem::allocate, py::arg("size"),
128  py::arg("options") = services::HostMem::Options(),
129  py::return_value_policy::take_ownership)
130  .def(
131  "map_memory",
132  [](HostMem &self, uintptr_t ptr, size_t size, HostMem::Options opts) {
133  return self.mapMemory(reinterpret_cast<void *>(ptr), size, opts);
134  },
135  py::arg("ptr"), py::arg("size"),
136  py::arg("options") = services::HostMem::Options())
137  .def(
138  "unmap_memory",
139  [](HostMem &self, uintptr_t ptr) {
140  return self.unmapMemory(reinterpret_cast<void *>(ptr));
141  },
142  py::arg("ptr"));
143 
144  py::class_<AppID>(m, "AppID")
145  .def(py::init<std::string, std::optional<uint32_t>>(), py::arg("name"),
146  py::arg("idx") = std::nullopt)
147  .def_property_readonly("name", [](AppID &id) { return id.name; })
148  .def_property_readonly("idx",
149  [](AppID &id) -> py::object {
150  if (id.idx)
151  return py::cast(id.idx);
152  return py::none();
153  })
154  .def("__repr__",
155  [](AppID &id) {
156  std::string ret = "<" + id.name;
157  if (id.idx)
158  ret = ret + "[" + std::to_string(*id.idx) + "]";
159  ret = ret + ">";
160  return ret;
161  })
162  .def("__eq__", [](AppID &a, AppID &b) { return a == b; })
163  .def("__hash__", [](AppID &id) {
164  // TODO: This is a bad hash function. Replace it.
165  return std::hash<std::string>{}(id.name) ^
166  (std::hash<uint32_t>{}(id.idx.value_or(-1)) << 1);
167  });
168 
169  py::class_<std::future<MessageData>>(m, "MessageDataFuture")
170  .def("valid", &std::future<MessageData>::valid)
171  .def("wait", &std::future<MessageData>::wait)
172  .def("get", [](std::future<MessageData> &f) {
173  MessageData data = f.get();
174  return py::bytearray((const char *)data.getBytes(), data.getSize());
175  });
176 
177  py::class_<ChannelPort>(m, "ChannelPort")
178  .def("connect", &ChannelPort::connect,
179  py::arg("buffer_size") = std::nullopt)
180  .def_property_readonly("type", &ChannelPort::getType,
181  py::return_value_policy::reference);
182 
183  py::class_<WriteChannelPort, ChannelPort>(m, "WriteChannelPort")
184  .def("write", [](WriteChannelPort &p, py::bytearray &data) {
185  py::buffer_info info(py::buffer(data).request());
186  std::vector<uint8_t> dataVec((uint8_t *)info.ptr,
187  (uint8_t *)info.ptr + info.size);
188  p.write(dataVec);
189  });
190  py::class_<ReadChannelPort, ChannelPort>(m, "ReadChannelPort")
191  .def(
192  "read",
193  [](ReadChannelPort &p) -> py::bytearray {
194  MessageData data;
195  p.read(data);
196  return py::bytearray((const char *)data.getBytes(), data.getSize());
197  },
198  "Read data from the channel. Blocking.")
199  .def("read_async", &ReadChannelPort::readAsync);
200 
201  py::class_<BundlePort>(m, "BundlePort")
202  .def_property_readonly("id", &BundlePort::getID)
203  .def_property_readonly("channels", &BundlePort::getChannels,
204  py::return_value_policy::reference)
205  .def("getWrite", &BundlePort::getRawWrite,
206  py::return_value_policy::reference)
207  .def("getRead", &BundlePort::getRawRead,
208  py::return_value_policy::reference);
209 
210  py::class_<ServicePort, BundlePort>(m, "ServicePort");
211  py::class_<FuncService::Function, ServicePort>(m, "Function")
212  .def(
213  "call",
214  [](FuncService::Function &self,
215  py::bytearray msg) -> std::future<MessageData> {
216  py::buffer_info info(py::buffer(msg).request());
217  std::vector<uint8_t> dataVec((uint8_t *)info.ptr,
218  (uint8_t *)info.ptr + info.size);
219  MessageData data(dataVec);
220  return self.call(data);
221  },
222  py::return_value_policy::take_ownership)
223  .def("connect", &FuncService::Function::connect);
224 
225  // Store this variable (not commonly done) as the "children" method needs for
226  // "Instance" to be defined first.
227  auto hwmodule =
228  py::class_<HWModule>(m, "HWModule")
229  .def_property_readonly("info", &HWModule::getInfo)
230  .def_property_readonly("ports", &HWModule::getPorts,
231  py::return_value_policy::reference);
232 
233  // In order to inherit methods from "HWModule", it needs to be defined first.
234  py::class_<Instance, HWModule>(m, "Instance")
235  .def_property_readonly("id", &Instance::getID);
236 
237  py::class_<Accelerator, HWModule>(m, "Accelerator");
238 
239  // Since this returns a vector of Instance*, we need to define Instance first
240  // or else pybind11-stubgen complains.
241  hwmodule.def_property_readonly("children", &HWModule::getChildren,
242  py::return_value_policy::reference);
243 
244  auto accConn = py::class_<AcceleratorConnection>(m, "AcceleratorConnection");
245 
246  py::class_<Context>(m, "Context")
247  .def(py::init<>())
248  .def("connect", &Context::connect);
249 
250  accConn.def(py::init(&registry::connect))
251  .def(
252  "sysinfo",
253  [](AcceleratorConnection &acc) {
254  return acc.getService<services::SysInfo>({});
255  },
256  py::return_value_policy::reference)
257  .def(
258  "get_service_mmio",
259  [](AcceleratorConnection &acc) {
260  return acc.getService<services::MMIO>({});
261  },
262  py::return_value_policy::reference)
263  .def(
264  "get_service_hostmem",
265  [](AcceleratorConnection &acc) {
266  return acc.getService<services::HostMem>({});
267  },
268  py::return_value_policy::reference);
269 
270  py::class_<Manifest>(m, "Manifest")
271  .def(py::init<Context &, std::string>())
272  .def_property_readonly("api_version", &Manifest::getApiVersion)
273  .def("build_accelerator", &Manifest::buildAccelerator,
274  py::return_value_policy::take_ownership)
275  .def_property_readonly("type_table", &Manifest::getTypeTable)
276  .def_property_readonly("module_infos", &Manifest::getModuleInfos);
277 }
Abstract class representing a connection to an accelerator.
Definition: Accelerator.h:78
ServiceClass * getService(AppIDPath id={}, std::string implName={}, ServiceImplDetails details={}, HWClientDetails clients={})
Get a typed reference to a particular service type.
Definition: Accelerator.h:99
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:35
WriteChannelPort & getRawWrite(const std::string &name) const
Get access to the raw byte streams of a channel.
Definition: Ports.cpp:25
const std::map< std::string, ChannelPort & > & getChannels() const
Definition: Ports.h:168
AppID getID() const
Get the ID of the port.
Definition: Ports.h:160
const ChannelVector & getChannels() const
Definition: Types.h:54
Unidirectional channels are the basic communication primitive between the host and accelerator.
Definition: Ports.h:33
virtual void connect(std::optional< unsigned > bufferSize=std::nullopt)
Set up a connection to the accelerator.
Definition: Ports.h:41
const Type * getType() const
Definition: Ports.h:46
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
std::unique_ptr< Accelerator > buildAccelerator(AcceleratorConnection &acc) const
Definition: Manifest.cpp:538
std::vector< ModuleInfo > getModuleInfos() const
Definition: Manifest.cpp:530
uint32_t getApiVersion() const
Definition: Manifest.cpp:526
const std::vector< const Type * > & getTypeTable() const
The Type Table is an ordered list of types.
Definition: Manifest.cpp:542
A logical chunk of data representing serialized data.
Definition: Common.h:86
A ChannelPort which reads data from the accelerator.
Definition: Ports.h:69
virtual std::future< MessageData > readAsync()
Asynchronous read.
Definition: Ports.cpp:77
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:57
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:166
virtual std::unique_ptr< HostMemRegion > allocate(std::size_t size, Options opts) const =0
Allocate a region of host memory in accelerator accessible address space.
virtual uint64_t read(uint32_t addr) const =0
virtual void write(uint32_t addr, uint64_t data)=0
Information about the Accelerator system.
Definition: Services.h:77
virtual std::string getJsonManifest() const
Return the JSON-formatted system manifest.
Definition: Services.cpp:32
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:62
const std::optional< std::string > repo
Definition: Common.h:61
const std::optional< std::string > version
Definition: Common.h:60
const std::optional< std::string > summary
Definition: Common.h:59
const std::optional< std::string > name
Definition: Common.h:58
RAII memory region for host memory.
Definition: Services.h:123
virtual void * getPtr() const =0
virtual std::size_t getSize() const =0
Options for allocating host memory.
Definition: Services.h:131
static const void * get(const ChannelPort *port, const std::type_info *&type)
Definition: esiCppAccel.cpp:33