Loading [MathJax]/extensions/tex2jax.js
CIRCT 22.0.0git
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
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#include "esi/Engines.h"
19
20#include "zlib.h"
21
22#include <cassert>
23#include <stdexcept>
24
25using namespace esi;
26using namespace esi::services;
27
29 std::string implName,
30 ServiceImplDetails details,
31 HWClientDetails clients) {
32 return conn.getService(service, id, implName, details, clients);
33}
34
35std::string SysInfo::getServiceSymbol() const { return "__builtin_SysInfo"; }
36
37// Allocate 10MB for the uncompressed manifest. This should be plenty.
38constexpr uint32_t MAX_MANIFEST_SIZE = 10 << 20;
39/// Get the compressed manifest, uncompress, and return it.
40std::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
57 const HWClientDetails &clients)
58 : Service(conn) {
59 AppIDPath idParent = idPath.parent();
60 for (const HWClientDetail &client : clients) {
61 auto offsetIter = client.implOptions.find("offset");
62 if (offsetIter == client.implOptions.end())
63 throw std::runtime_error("MMIO client missing 'offset' option");
64 Constant offset = std::any_cast<Constant>(offsetIter->second);
65 uint64_t offsetVal = std::any_cast<uint64_t>(offset.value);
66 if (offsetVal >= 1ull << 32)
67 throw std::runtime_error("MMIO client offset mustn't exceed 32 bits");
68
69 auto sizeIter = client.implOptions.find("size");
70 if (sizeIter == client.implOptions.end())
71 throw std::runtime_error("MMIO client missing 'size' option");
72 Constant size = std::any_cast<Constant>(sizeIter->second);
73 uint64_t sizeVal = std::any_cast<uint64_t>(size.value);
74 if (sizeVal >= 1ull << 32)
75 throw std::runtime_error("MMIO client size mustn't exceed 32 bits");
76 AppIDPath absPath = idParent + client.relPath;
77 regions[absPath] = RegionDescriptor{(uint32_t)offsetVal, (uint32_t)sizeVal};
78 }
79}
80
81std::string MMIO::getServiceSymbol() const {
82 return std::string(MMIO::StdName);
83}
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
92namespace {
93class MMIOPassThrough : public MMIO {
94public:
95 MMIOPassThrough(const HWClientDetails &clients, const AppIDPath &idPath,
96 MMIO *parent)
97 : MMIO(parent->getConnection(), idPath, clients), parent(parent) {}
98 uint64_t read(uint32_t addr) const override { return parent->read(addr); }
99 void write(uint32_t addr, uint64_t data) override {
100 parent->write(addr, data);
101 }
102
103private:
104 MMIO *parent;
105};
106} // namespace
107
109 std::string implName, ServiceImplDetails details,
110 HWClientDetails clients) {
111 if (service != typeid(MMIO))
112 return Service::getChildService(service, id, implName, details, clients);
113 return new MMIOPassThrough(clients, id, this);
114}
115
116//===----------------------------------------------------------------------===//
117// MMIO Region service port class implementations.
118//===----------------------------------------------------------------------===//
119
121 : ServicePort(id, nullptr, {}), parent(parent), desc(desc) {}
122uint64_t MMIO::MMIORegion::read(uint32_t addr) const {
123 if (addr >= desc.size)
124 throw std::runtime_error("MMIO read out of bounds: " + toHex(addr));
125 return parent->read(desc.base + addr);
126}
127void MMIO::MMIORegion::write(uint32_t addr, uint64_t data) {
128 if (addr >= desc.size)
129 throw std::runtime_error("MMIO write out of bounds: " + toHex(addr));
130 parent->write(desc.base + addr, data);
131}
132
134 : SysInfo(mmio->getConnection()), mmio(mmio) {}
135
137 uint64_t reg;
138 if ((reg = mmio->read(MetadataOffset)) != MagicNumber)
139 throw std::runtime_error("Invalid magic number: " + toHex(reg));
140 return mmio->read(MetadataOffset + 8);
141}
142
143std::vector<uint8_t> MMIOSysInfo::getCompressedManifest() const {
144 uint64_t version = getEsiVersion();
145 if (version != 0)
146 throw std::runtime_error("Unsupported ESI header version: " +
147 std::to_string(version));
148 uint64_t manifestPtr = mmio->read(MetadataOffset + 0x10);
149 uint64_t size = mmio->read(manifestPtr);
150 uint64_t numWords = (size + 7) / 8;
151 std::vector<uint64_t> manifestWords(numWords);
152 for (size_t i = 0; i < numWords; ++i)
153 manifestWords[i] = mmio->read(manifestPtr + 8 + (i * 8));
154
155 std::vector<uint8_t> manifest;
156 for (size_t i = 0; i < size; ++i) {
157 uint64_t word = manifestWords[i / 8];
158 manifest.push_back(word >> (8 * (i % 8)));
159 }
160 return manifest;
161}
162
163std::string HostMem::getServiceSymbol() const { return "__builtin_HostMem"; }
164
166 const ServiceImplDetails &details,
167 const HWClientDetails &clients)
168 : Service(conn), id(idPath) {
169 if (auto f = details.find("service"); f != details.end()) {
170 serviceSymbol = std::any_cast<std::string>(f->second);
171 // Strip off initial '@'.
172 serviceSymbol = serviceSymbol.substr(1);
173 }
174}
175
177 return new BundlePort(id.back(), type,
178 conn.getEngineMapFor(id).requestPorts(id, type));
179}
180
182 ServiceImplDetails details, HWClientDetails clients)
183 : Service(conn) {
184
185 if (auto f = details.find("service"); f != details.end())
186 // Strip off initial '@'.
187 symbol = std::any_cast<std::string>(f->second).substr(1);
188}
189
190std::string FuncService::getServiceSymbol() const { return symbol; }
191
193 return new Function(id.back(), type,
194 conn.getEngineMapFor(id).requestPorts(id, type));
195}
196
198 WriteChannelPort &arg,
199 ReadChannelPort &result) {
200 return new Function(
201 id, type, {{std::string("arg"), arg}, {std::string("result"), result}});
202 return nullptr;
203}
204
206 if (connected)
207 throw std::runtime_error("Function is already connected");
208 if (channels.size() != 2)
209 throw std::runtime_error("FuncService must have exactly two channels");
210 arg = &getRawWrite("arg");
211 arg->connect();
212 result = &getRawRead("result");
213 result->connect();
214 connected = true;
215}
216
217std::future<MessageData>
219 if (!connected)
220 throw std::runtime_error("Function must be 'connect'ed before calling");
221 std::scoped_lock<std::mutex> lock(callMutex);
222 arg->write(argData);
223 return result->readAsync();
224}
225
227 ServiceImplDetails details)
228 : Service(acc) {
229 if (auto f = details.find("service"); f != details.end())
230 // Strip off initial '@'.
231 symbol = std::any_cast<std::string>(f->second).substr(1);
232}
233
234std::string CallService::getServiceSymbol() const { return symbol; }
235
237 return new Callback(conn, id.back(), type,
238 conn.getEngineMapFor(id).requestPorts(id, type));
239}
240
242 const BundleType *type, PortMap channels)
243 : ServicePort(id, type, channels), acc(acc) {}
244
246 AppID id,
247 const BundleType *type,
248 WriteChannelPort &result,
249 ReadChannelPort &arg) {
250 return new Callback(acc, id, type, {{"arg", arg}, {"result", result}});
251}
252
254 std::function<MessageData(const MessageData &)> callback, bool quick) {
255 if (channels.size() != 2)
256 throw std::runtime_error("CallService must have exactly two channels");
257 result = &getRawWrite("result");
258 result->connect();
259 arg = &getRawRead("arg");
260 if (quick) {
261 // If it's quick, we can just call the callback directly.
262 arg->connect([this, callback](MessageData argMsg) -> bool {
263 MessageData resultMsg = callback(std::move(argMsg));
264 this->result->write(std::move(resultMsg));
265 return true;
266 });
267 } else {
268 // If it's not quick, we need to use the service thread.
269 arg->connect();
270 acc.getServiceThread()->addListener(
271 {arg}, [this, callback](ReadChannelPort *, MessageData argMsg) -> void {
272 MessageData resultMsg = callback(std::move(argMsg));
273 this->result->write(std::move(resultMsg));
274 });
275 }
276}
277
283
285 return std::string(TelemetryService::StdName);
286}
287
289 const BundleType *type) const {
290 auto *port = new Telemetry(id.back(), type,
291 conn.getEngineMapFor(id).requestPorts(id, type));
292 telemetryPorts.insert(std::make_pair(id, port));
293 return port;
294}
295
297 PortMap channels)
298 : ServicePort(id, type, channels) {}
299
302 WriteChannelPort &get, ReadChannelPort &data) {
303 return new Telemetry(id, type, {{"get", get}, {"data", data}});
304}
305
306/// Connect to a particular telemetry port. The bundle should have two channels
307/// -- get and data. Get should have type 'i0' and data can be anything.
309 if (channels.size() != 2)
310 throw std::runtime_error("TelemetryService must have exactly two channels");
311 get_req = &getRawWrite("get");
312 // TODO: There are problems with DMA'ing i0. As a workaround, sometimes i1 is
313 // used. When these issues are fixed, re-enable this check. There may also be
314 // a problem with the void type.
315 // if (!dynamic_cast<const VoidType *>(get_req->getType()))
316 // throw std::runtime_error("TelemetryService get channel must be void");
317 get_req->connect();
318 data = &getRawRead("data");
319 data->connect();
320}
321
322std::future<MessageData> TelemetryService::Telemetry::read() {
323 if (!get_req)
324 throw std::runtime_error("TelemetryService get channel not connected");
325 // TODO: This is a hack to get around the fact that we can't send a void
326 // message. We need to send something, so we send a single byte whose value
327 // doesn't matter.
328 std::vector<uint8_t> empty = {1};
329 get_req->write(MessageData(empty));
330 return data->readAsync();
331}
332
334 Service::Type svcType, AppIDPath id,
335 std::string implName,
336 ServiceImplDetails details,
337 HWClientDetails clients) {
338 // TODO: Add a proper registration mechanism.
339 if (svcType == typeid(FuncService))
340 return new FuncService(id, *acc, details, clients);
341 if (svcType == typeid(CallService))
342 return new CallService(*acc, id, details);
343 if (svcType == typeid(TelemetryService))
344 return new TelemetryService(id, *acc, details, clients);
345 if (svcType == typeid(CustomService))
346 return new CustomService(id, *acc, details, clients);
347 return nullptr;
348}
349
351 // TODO: Add a proper registration mechanism.
352 if (svcName == "esi.service.std.func")
353 return typeid(FuncService);
354 if (svcName == "esi.service.std.call")
355 return typeid(CallService);
356 if (svcName == MMIO::StdName)
357 return typeid(MMIO);
358 if (svcName == HostMem::StdName)
359 return typeid(HostMem);
360 if (svcName == TelemetryService::StdName)
361 return typeid(TelemetryService);
362 return typeid(CustomService);
363}
constexpr uint32_t MAX_MANIFEST_SIZE
Definition Services.cpp:38
static InstancePath empty
Abstract class representing a connection to an accelerator.
Definition Accelerator.h:79
ServiceClass * getService(AppIDPath id={}, std::string implName={}, ServiceImplDetails details={}, HWClientDetails clients={})
Get a typed reference to a particular service type.
virtual const BundleEngineMap & getEngineMapFor(AppIDPath id)
AppIDPath parent() const
Definition Manifest.cpp:733
PortMap requestPorts(const AppIDPath &idPath, const BundleType *bundleType) const
Request ports for all the channels in a bundle.
Definition Engines.cpp:468
Services provide connections to 'bundles' – collections of named, unidirectional communication channe...
Definition Ports.h:226
const BundleType * type
Definition Ports.h:269
Bundles represent a collection of channels.
Definition Types.h:44
A logical chunk of data representing serialized data.
Definition Common.h:104
A ChannelPort which reads data from the accelerator.
Definition Ports.h:124
A ChannelPort which sends data to the accelerator.
Definition Ports.h:77
A function call which gets attached to a service port.
Definition Services.h:306
static Callback * get(AcceleratorConnection &acc, AppID id, const BundleType *type, WriteChannelPort &result, ReadChannelPort &arg)
Definition Services.cpp:245
Callback(AcceleratorConnection &acc, AppID id, const BundleType *, PortMap channels)
Definition Services.cpp:241
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:253
Service for servicing function calls from the accelerator.
Definition Services.h:296
virtual std::string getServiceSymbol() const override
Definition Services.cpp:234
CallService(AcceleratorConnection &acc, AppIDPath id, ServiceImplDetails details)
Definition Services.cpp:226
virtual BundlePort * getPort(AppIDPath id, const BundleType *type) const override
Get specialized port for this service to attach to the given appid path.
Definition Services.cpp:236
A service for which there are no standard services registered.
Definition Services.h:81
virtual BundlePort * getPort(AppIDPath id, const BundleType *type) const override
Get specialized port for this service to attach to the given appid path.
Definition Services.cpp:176
CustomService(AppIDPath idPath, AcceleratorConnection &, const ServiceImplDetails &details, const HWClientDetails &clients)
Definition Services.cpp:165
A function call which gets attached to a service port.
Definition Services.h:263
std::future< MessageData > call(const MessageData &arg)
Definition Services.cpp:218
static Function * get(AppID id, BundleType *type, WriteChannelPort &arg, ReadChannelPort &result)
Definition Services.cpp:197
Service for calling functions.
Definition Services.h:253
virtual std::string getServiceSymbol() const override
Definition Services.cpp:190
virtual BundlePort * getPort(AppIDPath id, const BundleType *type) const override
Get specialized port for this service to attach to the given appid path.
Definition Services.cpp:192
FuncService(AppIDPath id, AcceleratorConnection &, ServiceImplDetails details, HWClientDetails clients)
Definition Services.cpp:181
virtual std::string getServiceSymbol() const override
Definition Services.cpp:163
static constexpr std::string_view StdName
Definition Services.h:201
virtual std::vector< uint8_t > getCompressedManifest() const override
Return the zlib compressed JSON system manifest.
Definition Services.cpp:143
uint32_t getEsiVersion() const override
Get the ESI version number to check version compatibility.
Definition Services.cpp:136
A "slice" of some parent MMIO space.
Definition Services.h:160
virtual uint64_t read(uint32_t addr) const
Read a 64-bit value from this region, not the global address space.
Definition Services.cpp:122
MMIORegion(AppID id, MMIO *parent, RegionDescriptor desc)
Definition Services.cpp:120
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:127
virtual uint64_t read(uint32_t addr) const =0
Read a 64-bit value from the global MMIO space.
MMIO(AcceleratorConnection &, const AppIDPath &idPath, const HWClientDetails &clients)
Definition Services.cpp:56
virtual BundlePort * getPort(AppIDPath id, const BundleType *type) const override
Get a MMIO region port for a particular region descriptor.
Definition Services.cpp:84
std::map< AppIDPath, RegionDescriptor > regions
MMIO base address table.
Definition Services.h:156
static constexpr std::string_view StdName
Definition Services.h:119
virtual Service * getChildService(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:108
virtual std::string getServiceSymbol() const override
Definition Services.cpp:81
Add a custom interface to a service client at a particular point in the design hierarchy.
Definition Services.h:36
static Service::Type lookupServiceType(const std::string &)
Resolve a service type from a string.
Definition Services.cpp:350
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:333
Parent class of all APIs modeled as 'services'.
Definition Services.h:46
AcceleratorConnection & getConnection() const
Definition Services.h:72
const std::type_info & Type
Definition Services.h:48
virtual Service * getChildService(Service::Type service, AppIDPath id={}, std::string implName={}, ServiceImplDetails details={}, HWClientDetails clients={})
Create a "child" service of this service.
Definition Services.cpp:28
AcceleratorConnection & conn
Definition Services.h:75
Information about the Accelerator system.
Definition Services.h:100
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
A telemetry port which gets attached to a service port.
Definition Services.h:356
static Telemetry * get(AppID id, BundleType *type, WriteChannelPort &get, ReadChannelPort &data)
Definition Services.cpp:301
std::future< MessageData > read()
Definition Services.cpp:322
Telemetry(AppID id, const BundleType *type, PortMap channels)
Definition Services.cpp:296
void connect()
Connect to a particular telemetry port.
Definition Services.cpp:308
Service for retrieving telemetry data from the accelerator.
Definition Services.h:344
std::map< AppIDPath, Telemetry * > telemetryPorts
Definition Services.h:384
static constexpr std::string_view StdName
Definition Services.h:346
TelemetryService(AppIDPath id, AcceleratorConnection &, ServiceImplDetails details, HWClientDetails clients)
Definition Services.cpp:278
virtual std::string getServiceSymbol() const override
Definition Services.cpp:284
virtual BundlePort * getPort(AppIDPath id, const BundleType *type) const override
Get specialized port for this service to attach to the given appid path.
Definition Services.cpp:288
Definition esi.py:1
constexpr uint64_t MagicNumber
Definition Accelerator.h:48
std::map< std::string, std::any > ServiceImplDetails
Definition Common.h:99
std::string toHex(void *val)
Definition Common.cpp:37
constexpr uint32_t MetadataOffset
Definition Accelerator.h:45
std::map< std::string, ChannelPort & > PortMap
Definition Ports.h:29
std::vector< HWClientDetail > HWClientDetails
Definition Common.h:98
std::any value
Definition Common.h:59
A description of a hardware client.
Definition Common.h:92
Describe a region (slice) of MMIO space.
Definition Services.h:122