CIRCT  20.0.0git
Services.cpp
Go to the documentation of this file.
1 //===- StdServices.cpp - implementations of std services ------------------===//
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 // DO NOT EDIT!
10 // This file is distributed as part of an ESI package. The source for this file
11 // should always be modified within CIRCT
12 // (lib/dialect/ESI/runtime/cpp/lib/backends/Cosim.cpp).
13 //
14 //===----------------------------------------------------------------------===//
15 
16 #include "esi/Services.h"
17 #include "esi/Accelerator.h"
18 
19 #include "zlib.h"
20 
21 #include <cassert>
22 #include <stdexcept>
23 
24 using namespace esi;
25 using namespace esi::services;
26 
28  Service::Type service, AppIDPath id,
29  std::string implName,
30  ServiceImplDetails details,
31  HWClientDetails clients) {
32  return conn->getService(service, id, implName, details, clients);
33 }
34 
35 std::string SysInfo::getServiceSymbol() const { return "__builtin_SysInfo"; }
36 
37 // Allocate 10MB for the uncompressed manifest. This should be plenty.
38 constexpr uint32_t MAX_MANIFEST_SIZE = 10 << 20;
39 /// Get the compressed manifest, uncompress, and return it.
40 std::string SysInfo::getJsonManifest() const {
41  std::vector<uint8_t> compressed = getCompressedManifest();
42  std::vector<Bytef> dst(MAX_MANIFEST_SIZE);
43  uLongf dstSize = MAX_MANIFEST_SIZE;
44  int rc =
45  uncompress(dst.data(), &dstSize, compressed.data(), compressed.size());
46  if (rc != Z_OK)
47  throw std::runtime_error("zlib uncompress failed with rc=" +
48  std::to_string(rc));
49  return std::string(reinterpret_cast<char *>(dst.data()), dstSize);
50 }
51 
52 //===----------------------------------------------------------------------===//
53 // MMIO class implementations.
54 //===----------------------------------------------------------------------===//
55 
56 MMIO::MMIO(Context &ctxt, AppIDPath idPath, std::string implName,
57  const ServiceImplDetails &details, const HWClientDetails &clients) {
58  for (const HWClientDetail &client : clients) {
59  auto offsetIter = client.implOptions.find("offset");
60  if (offsetIter == client.implOptions.end())
61  throw std::runtime_error("MMIO client missing 'offset' option");
62  Constant offset = std::any_cast<Constant>(offsetIter->second);
63  uint64_t offsetVal = std::any_cast<uint64_t>(offset.value);
64  if (offsetVal >= 1ull << 32)
65  throw std::runtime_error("MMIO client offset mustn't exceed 32 bits");
66 
67  auto sizeIter = client.implOptions.find("size");
68  if (sizeIter == client.implOptions.end())
69  throw std::runtime_error("MMIO client missing 'size' option");
70  Constant size = std::any_cast<Constant>(sizeIter->second);
71  uint64_t sizeVal = std::any_cast<uint64_t>(size.value);
72  if (sizeVal >= 1ull << 32)
73  throw std::runtime_error("MMIO client size mustn't exceed 32 bits");
74  regions[client.relPath] =
75  RegionDescriptor{(uint32_t)offsetVal, (uint32_t)sizeVal};
76  }
77 }
78 
79 std::string MMIO::getServiceSymbol() const {
80  return std::string(MMIO::StdName);
81 }
83  const std::map<std::string, ChannelPort &> &,
84  AcceleratorConnection &conn) const {
85  auto regionIter = regions.find(id);
86  if (regionIter == regions.end())
87  return nullptr;
88  return new MMIORegion(id.back(), const_cast<MMIO *>(this),
89  regionIter->second);
90 }
91 
92 namespace {
93 class MMIOPassThrough : public MMIO {
94 public:
95  MMIOPassThrough(Context &ctxt, AppIDPath idPath, std::string implName,
96  const ServiceImplDetails &details,
97  const HWClientDetails &clients, MMIO *parent)
98  : MMIO(ctxt, idPath, implName, details, clients), parent(parent) {}
99  uint64_t read(uint32_t addr) const override { return parent->read(addr); }
100  void write(uint32_t addr, uint64_t data) override {
101  parent->write(addr, data);
102  }
103 
104 private:
105  MMIO *parent;
106 };
107 } // namespace
108 
110  Service::Type service, AppIDPath id,
111  std::string implName, ServiceImplDetails details,
112  HWClientDetails clients) {
113  if (service != typeid(MMIO))
114  return Service::getChildService(conn, service, id, implName, details,
115  clients);
116  return new MMIOPassThrough(conn->getCtxt(), id, implName, details, clients,
117  this);
118 }
119 
120 //===----------------------------------------------------------------------===//
121 // MMIO Region service port class implementations.
122 //===----------------------------------------------------------------------===//
123 
125  : ServicePort(id, {}), parent(parent), desc(desc) {}
126 uint64_t MMIO::MMIORegion::read(uint32_t addr) const {
127  if (addr >= desc.size)
128  throw std::runtime_error("MMIO read out of bounds: " + toHex(addr));
129  return parent->read(desc.base + addr);
130 }
131 void MMIO::MMIORegion::write(uint32_t addr, uint64_t data) {
132  if (addr >= desc.size)
133  throw std::runtime_error("MMIO write out of bounds: " + toHex(addr));
134  parent->write(desc.base + addr, data);
135 }
136 
137 MMIOSysInfo::MMIOSysInfo(const MMIO *mmio) : mmio(mmio) {}
138 
139 uint32_t MMIOSysInfo::getEsiVersion() const {
140  uint64_t reg;
141  if ((reg = mmio->read(MetadataOffset)) != MagicNumber)
142  throw std::runtime_error("Invalid magic number: " + toHex(reg));
143  return mmio->read(MetadataOffset + 8);
144 }
145 
146 std::vector<uint8_t> MMIOSysInfo::getCompressedManifest() const {
147  uint64_t version = getEsiVersion();
148  if (version != 0)
149  throw std::runtime_error("Unsupported ESI header version: " +
150  std::to_string(version));
151  uint64_t manifestPtr = mmio->read(MetadataOffset + 0x10);
152  uint64_t size = mmio->read(manifestPtr);
153  uint64_t numWords = (size + 7) / 8;
154  std::vector<uint64_t> manifestWords(numWords);
155  for (size_t i = 0; i < numWords; ++i)
156  manifestWords[i] = mmio->read(manifestPtr + 8 + (i * 8));
157 
158  std::vector<uint8_t> manifest;
159  for (size_t i = 0; i < size; ++i) {
160  uint64_t word = manifestWords[i / 8];
161  manifest.push_back(word >> (8 * (i % 8)));
162  }
163  return manifest;
164 }
165 
166 std::string HostMem::getServiceSymbol() const { return "__builtin_HostMem"; }
167 
169  const ServiceImplDetails &details,
170  const HWClientDetails &clients)
171  : id(idPath) {
172  if (auto f = details.find("service"); f != details.end()) {
173  serviceSymbol = std::any_cast<std::string>(f->second);
174  // Strip off initial '@'.
175  serviceSymbol = serviceSymbol.substr(1);
176  }
177 }
178 
180  const std::string &implName,
181  ServiceImplDetails details, HWClientDetails clients) {
182  if (auto f = details.find("service"); f != details.end())
183  // Strip off initial '@'.
184  symbol = std::any_cast<std::string>(f->second).substr(1);
185 }
186 
187 std::string FuncService::getServiceSymbol() const { return symbol; }
188 
189 ServicePort *
191  const std::map<std::string, ChannelPort &> &channels,
192  AcceleratorConnection &acc) const {
193  return new Function(id.back(), channels);
194 }
195 
197  AppID id, const std::map<std::string, ChannelPort &> &channels)
198  : ServicePort(id, channels),
199  arg(dynamic_cast<WriteChannelPort &>(channels.at("arg"))),
200  result(dynamic_cast<ReadChannelPort &>(channels.at("result"))) {
201  assert(channels.size() == 2 && "FuncService must have exactly two channels");
202 }
203 
205  WriteChannelPort &arg,
206  ReadChannelPort &result) {
207  return new Function(id, {{"arg", arg}, {"result", result}});
208 }
209 
211  arg.connect();
212  result.connect();
213 }
214 
215 std::future<MessageData>
217  std::scoped_lock<std::mutex> lock(callMutex);
218  arg.write(argData);
219  return result.readAsync();
220 }
221 
223  std::string implName, ServiceImplDetails details,
224  HWClientDetails clients) {
225  if (auto f = details.find("service"); f != details.end())
226  // Strip off initial '@'.
227  symbol = std::any_cast<std::string>(f->second).substr(1);
228 }
229 
230 std::string CallService::getServiceSymbol() const { return symbol; }
231 
232 ServicePort *
234  const std::map<std::string, ChannelPort &> &channels,
235  AcceleratorConnection &acc) const {
236  return new Callback(acc, id.back(), channels);
237 }
238 
239 ReadChannelPort &getRead(const std::map<std::string, ChannelPort &> &channels,
240  const std::string &name) {
241  auto f = channels.find(name);
242  if (f == channels.end())
243  throw std::runtime_error("CallService must have an '" + name + "' channel");
244  return dynamic_cast<ReadChannelPort &>(f->second);
245 }
246 
247 WriteChannelPort &getWrite(const std::map<std::string, ChannelPort &> &channels,
248  const std::string &name) {
249  auto f = channels.find(name);
250  if (f == channels.end())
251  throw std::runtime_error("CallService must have an '" + name + "' channel");
252  return dynamic_cast<WriteChannelPort &>(f->second);
253 }
254 
256  AcceleratorConnection &acc, AppID id,
257  const std::map<std::string, ChannelPort &> &channels)
258  : ServicePort(id, channels), arg(getRead(channels, "arg")),
259  result(getWrite(channels, "result")), acc(acc) {
260  if (channels.size() != 2)
261  throw std::runtime_error("CallService must have exactly two channels");
262 }
263 
265  AppID id,
266  WriteChannelPort &result,
267  ReadChannelPort &arg) {
268  return new Callback(acc, id, {{"arg", arg}, {"result", result}});
269 }
270 
272  std::function<MessageData(const MessageData &)> callback, bool quick) {
273  result.connect();
274  if (quick) {
275  // If it's quick, we can just call the callback directly.
276  arg.connect([this, callback](MessageData argMsg) -> bool {
277  MessageData resultMsg = callback(std::move(argMsg));
278  this->result.write(std::move(resultMsg));
279  return true;
280  });
281  } else {
282  // If it's not quick, we need to use the service thread.
283  arg.connect();
284  acc.getServiceThread()->addListener(
285  {&arg},
286  [this, callback](ReadChannelPort *, MessageData argMsg) -> void {
287  MessageData resultMsg = callback(std::move(argMsg));
288  this->result.write(std::move(resultMsg));
289  });
290  }
291 }
292 
294  Service::Type svcType, AppIDPath id,
295  std::string implName,
296  ServiceImplDetails details,
297  HWClientDetails clients) {
298  // TODO: Add a proper registration mechanism.
299  if (svcType == typeid(FuncService))
300  return new FuncService(acc, id, implName, details, clients);
301  if (svcType == typeid(CallService))
302  return new CallService(acc, id, implName, details, clients);
303  return nullptr;
304 }
305 
307  // TODO: Add a proper registration mechanism.
308  if (svcName == "esi.service.std.func")
309  return typeid(FuncService);
310  if (svcName == "esi.service.std.call")
311  return typeid(CallService);
312  if (svcName == MMIO::StdName)
313  return typeid(MMIO);
314  return typeid(CustomService);
315 }
assert(baseType &&"element must be base type")
WriteChannelPort & getWrite(const std::map< std::string, ChannelPort & > &channels, const std::string &name)
Definition: Services.cpp:247
constexpr uint32_t MAX_MANIFEST_SIZE
Definition: Services.cpp:38
ReadChannelPort & getRead(const std::map< std::string, ChannelPort & > &channels, const std::string &name)
Definition: Services.cpp:239
Abstract class representing a connection to an accelerator.
Definition: Accelerator.h:78
Context & getCtxt() const
Definition: Accelerator.h:82
ServiceClass * getService(AppIDPath id={}, std::string implName={}, ServiceImplDetails details={}, HWClientDetails clients={})
Get a typed reference to a particular service type.
Definition: Accelerator.h:112
std::map< std::string, ChannelPort & > channels
Definition: Ports.h:229
Bundles represent a collection of channels.
Definition: Types.h:44
AcceleratorConnections, Accelerators, and Manifests must all share a context.
Definition: Context.h:31
A logical chunk of data representing serialized data.
Definition: Common.h:103
A ChannelPort which reads data from the accelerator.
Definition: Ports.h:103
A ChannelPort which sends data to the accelerator.
Definition: Ports.h:74
A function call which gets attached to a service port.
Definition: Services.h:288
static Callback * get(AcceleratorConnection &acc, AppID id, WriteChannelPort &result, ReadChannelPort &arg)
Definition: Services.cpp:264
Callback(AcceleratorConnection &acc, AppID id, const std::map< std::string, ChannelPort & > &channels)
Definition: Services.cpp:255
void connect(std::function< MessageData(const MessageData &)> callback, bool quick=false)
Connect a callback to code which will be executed when the accelerator invokes the callback.
Definition: Services.cpp:271
Service for servicing function calls from the accelerator.
Definition: Services.h:277
CallService(AcceleratorConnection *acc, AppIDPath id, std::string implName, ServiceImplDetails details, HWClientDetails clients)
Definition: Services.cpp:222
virtual std::string getServiceSymbol() const override
Definition: Services.cpp:230
virtual ServicePort * getPort(AppIDPath id, const BundleType *type, const std::map< std::string, ChannelPort & > &, AcceleratorConnection &) const override
Get specialized port for this service to attach to the given appid path.
Definition: Services.cpp:233
A service for which there are no standard services registered.
Definition: Services.h:77
CustomService(AppIDPath idPath, const ServiceImplDetails &details, const HWClientDetails &clients)
Definition: Services.cpp:168
A function call which gets attached to a service port.
Definition: Services.h:247
std::future< MessageData > call(const MessageData &arg)
Definition: Services.cpp:216
static Function * get(AppID id, WriteChannelPort &arg, ReadChannelPort &result)
Definition: Services.cpp:204
Function(AppID id, const std::map< std::string, ChannelPort & > &channels)
Definition: Services.cpp:196
Service for calling functions.
Definition: Services.h:235
virtual std::string getServiceSymbol() const override
Definition: Services.cpp:187
virtual ServicePort * getPort(AppIDPath id, const BundleType *type, const std::map< std::string, ChannelPort & > &, AcceleratorConnection &) const override
Get specialized port for this service to attach to the given appid path.
Definition: Services.cpp:190
FuncService(AcceleratorConnection *acc, AppIDPath id, const std::string &implName, ServiceImplDetails details, HWClientDetails clients)
Definition: Services.cpp:179
virtual std::string getServiceSymbol() const override
Definition: Services.cpp:166
virtual std::vector< uint8_t > getCompressedManifest() const override
Return the zlib compressed JSON system manifest.
Definition: Services.cpp:146
uint32_t getEsiVersion() const override
Get the ESI version number to check version compatibility.
Definition: Services.cpp:139
MMIOSysInfo(const MMIO *)
Definition: Services.cpp:137
A "slice" of some parent MMIO space.
Definition: Services.h:155
virtual uint64_t read(uint32_t addr) const
Read a 64-bit value from this region, not the global address space.
Definition: Services.cpp:126
MMIORegion(AppID id, MMIO *parent, RegionDescriptor desc)
Definition: Services.cpp:124
virtual void write(uint32_t addr, uint64_t data)
Write a 64-bit value to this region, not the global address space.
Definition: Services.cpp:131
virtual uint64_t read(uint32_t addr) const =0
Read a 64-bit value from the global MMIO space.
virtual Service * getChildService(AcceleratorConnection *conn, Service::Type service, AppIDPath id={}, std::string implName={}, ServiceImplDetails details={}, HWClientDetails clients={}) override
If the service is a MMIO service, return a region of the MMIO space which peers into ours.
Definition: Services.cpp:109
std::map< AppIDPath, RegionDescriptor > regions
MMIO base address table.
Definition: Services.h:151
static constexpr std::string_view StdName
Definition: Services.h:111
virtual ServicePort * getPort(AppIDPath id, const BundleType *type, const std::map< std::string, ChannelPort & > &, AcceleratorConnection &) const override
Get a MMIO region port for a particular region descriptor.
Definition: Services.cpp:82
virtual std::string getServiceSymbol() const override
Definition: Services.cpp:79
Add a custom interface to a service client at a particular point in the design hierarchy.
Definition: Services.h:35
static Service::Type lookupServiceType(const std::string &)
Resolve a service type from a string.
Definition: Services.cpp:306
static Service * createService(AcceleratorConnection *acc, Service::Type svcType, AppIDPath id, std::string implName, ServiceImplDetails details, HWClientDetails clients)
Create a service instance from the given details.
Definition: Services.cpp:293
Parent class of all APIs modeled as 'services'.
Definition: Services.h:45
const std::type_info & Type
Definition: Services.h:47
virtual Service * getChildService(AcceleratorConnection *conn, Service::Type service, AppIDPath id={}, std::string implName={}, ServiceImplDetails details={}, HWClientDetails clients={})
Create a "child" service of this service.
Definition: Services.cpp:27
virtual std::string getJsonManifest() const
Return the JSON-formatted system manifest.
Definition: Services.cpp:40
virtual std::vector< uint8_t > getCompressedManifest() const =0
Return the zlib compressed JSON system manifest.
virtual std::string getServiceSymbol() const override
Definition: Services.cpp:35
Definition: esi.py:1
constexpr uint64_t MagicNumber
Definition: Accelerator.h:47
std::map< std::string, std::any > ServiceImplDetails
Definition: Common.h:98
std::string toHex(uint32_t val)
Definition: Common.cpp:37
constexpr uint32_t MetadataOffset
Definition: Accelerator.h:44
std::vector< HWClientDetail > HWClientDetails
Definition: Common.h:97
def reg(value, clock, reset=None, reset_value=None, name=None, sym_name=None)
Definition: seq.py:21
std::any value
Definition: Common.h:58
A description of a hardware client.
Definition: Common.h:91
Describe a region (slice) of MMIO space.
Definition: Services.h:114