31class UnknownEngine :
public Engine {
34 :
Engine(conn), engineName(engineName) {}
37 throw std::runtime_error(
"Unknown engine '" + engineName +
"'");
41 const std::string &channelName,
43 const Type *type)
override {
44 if (BundlePort::isWrite(dir))
45 return std::make_unique<UnknownWriteChannelPort>(
47 "Unknown engine '" + engineName +
"': cannot create write port");
49 return std::make_unique<UnknownReadChannelPort>(
50 type,
"Unknown engine '" + engineName +
"': cannot create read port");
54 std::string engineName;
69class OneItemBuffersToHost;
73 static constexpr size_t BufferPtrOffset = 8;
75 OneItemBuffersToHostReadPort(
const Type *type, OneItemBuffersToHost *engine,
76 AppIDPath idPath,
const std::string &channelName)
78 channelName(channelName) {
83 void writeBufferPtr();
89 std::string identifier()
const {
return idPath.
toStr() +
"." + channelName; }
95 OneItemBuffersToHost *engine;
97 std::unique_ptr<services::HostMem::HostMemRegion> buffer;
99 uint64_t pollCount = 0;
103 std::string channelName;
106class OneItemBuffersToHost :
public Engine {
107 friend class OneItemBuffersToHostReadPort;
112 :
Engine(conn), thisPath(idPath) {
114 auto mmioIDIter = details.find(
"mmio");
115 if (mmioIDIter != details.end())
116 mmioID = std::any_cast<AppID>(mmioIDIter->second);
123 return std::make_unique<OneItemBuffersToHost>(conn, idPath, details);
132 const std::string &channelName,
134 const Type *type)
override {
135 if (BundlePort::isWrite(dir))
136 return std::make_unique<UnknownWriteChannelPort>(
137 type, idPath.
toStr() +
"." + channelName +
138 " OneItemBuffersToHost: cannot create write port");
139 return std::make_unique<OneItemBuffersToHostReadPort>(type,
this, idPath,
145 std::optional<AppID> mmioID;
151void OneItemBuffersToHost::connect() {
156 throw std::runtime_error(
"OneItemBuffersToHost: no mmio path specified");
159 throw std::runtime_error(
"OneItemBuffersToHost: no host memory service");
166 mmioPath.push_back(*mmioID);
170 throw std::runtime_error(
172 " OneItemBuffersToHost: could not find MMIO port at " +
176 throw std::runtime_error(
178 " OneItemBuffersToHost: MMIO port is not an MMIO port");
184void OneItemBuffersToHostReadPort::writeBufferPtr() {
185 uint8_t *bufferData =
reinterpret_cast<uint8_t *
>(buffer->getPtr());
186 bufferData[bufferSize - 1] = 0;
188 engine->mmio->write(BufferPtrOffset,
189 reinterpret_cast<uint64_t
>(buffer->getDevicePtr()));
192void OneItemBuffersToHostReadPort::connectImpl(
195 buffer = engine->hostMem->allocate(bufferSize, {
true,
false});
199bool OneItemBuffersToHostReadPort::pollImpl() {
201 uint8_t *bufferData =
reinterpret_cast<uint8_t *
>(buffer->getPtr());
202 if (bufferData[bufferSize - 1] == 0)
205 Logger &logger = engine->conn.getLogger();
211 [
this, data](std::string &subsystem, std::string &msg,
212 std::unique_ptr<std::map<std::string, std::any>> &details) {
213 subsystem =
"OneItemBuffersToHost";
214 msg =
"recieved message";
215 details = std::make_unique<std::map<std::string, std::any>>();
216 (*details)[
"channel"] = identifier();
217 (*details)[
"data_size"] =
data.getSize();
218 (*details)[
"message_data"] =
data.toHex();
220 if (callback(std::move(data))) {
223 [
this](std::string &subsystem, std::string &msg,
224 std::unique_ptr<std::map<std::string, std::any>> &details) {
225 subsystem =
"OneItemBuffersToHost";
226 msg =
"callback accepted data";
227 details = std::make_unique<std::map<std::string, std::any>>();
228 (*details)[
"channel"] = identifier();
233 [
this](std::string &subsystem, std::string &msg,
234 std::unique_ptr<std::map<std::string, std::any>> &details) {
235 subsystem =
"OneItemBuffersToHost";
236 msg =
"callback rejected data";
237 details = std::make_unique<std::map<std::string, std::any>>();
238 (*details)[
"channel"] = identifier();
266class OneItemBuffersFromHost;
270 static constexpr size_t BufferPtrOffset = 8;
271 static constexpr size_t CompletionPtrOffset = 16;
273 OneItemBuffersFromHostWritePort(
const Type *type,
274 OneItemBuffersFromHost *engine,
276 const std::string &channelName)
278 channelName(channelName) {
285 std::string identifier()
const {
return idPath.
toStr() +
"." + channelName; }
289 bool tryWriteImpl(
const MessageData &data)
override;
294 OneItemBuffersFromHost *engine;
296 std::unique_ptr<services::HostMem::HostMemRegion> data_buffer;
297 std::unique_ptr<services::HostMem::HostMemRegion> completion_buffer;
300 std::mutex bufferMutex;
304 std::string channelName;
307class OneItemBuffersFromHost :
public Engine {
308 friend class OneItemBuffersFromHostWritePort;
313 :
Engine(conn), thisPath(idPath) {
315 auto mmioIDIter = details.find(
"mmio");
316 if (mmioIDIter != details.end())
317 mmioID = std::any_cast<AppID>(mmioIDIter->second);
324 return std::make_unique<OneItemBuffersFromHost>(conn, idPath, details);
332 std::unique_ptr<ChannelPort> createPort(
AppIDPath idPath,
333 const std::string &channelName,
335 const Type *type)
override {
336 if (!BundlePort::isWrite(dir))
337 return std::make_unique<UnknownReadChannelPort>(
338 type, idPath.
toStr() +
"." + channelName +
339 " OneItemBuffersFromHost: cannot create read port");
340 return std::make_unique<OneItemBuffersFromHostWritePort>(type,
this, idPath,
346 std::optional<AppID> mmioID;
353void OneItemBuffersFromHost::connect() {
358 throw std::runtime_error(
"OneItemBuffersFromHost: no mmio path specified");
361 throw std::runtime_error(
"OneItemBuffersFromHost: no host memory service");
368 mmioPath.push_back(*mmioID);
372 throw std::runtime_error(
374 " OneItemBuffersFromHost: could not find MMIO port at " +
378 throw std::runtime_error(
380 " OneItemBuffersFromHost: MMIO port is not an MMIO port");
386void OneItemBuffersFromHostWritePort::connectImpl(
389 data_buffer = engine->hostMem->allocate(std::max(bufferSize, (
size_t)512),
391 completion_buffer = engine->hostMem->allocate(512, {
true,
false});
394 *
static_cast<uint8_t *
>(completion_buffer->getPtr()) = 1;
397void OneItemBuffersFromHostWritePort::writeImpl(
const MessageData &data) {
398 while (!tryWriteImpl(data))
400 std::this_thread::yield();
403bool OneItemBuffersFromHostWritePort::tryWriteImpl(
const MessageData &data) {
404 Logger &logger = engine->conn.getLogger();
407 completion_buffer->flush();
408 uint8_t *completion =
409 reinterpret_cast<uint8_t *
>(completion_buffer->getPtr());
410 if (*completion == 0) {
412 [
this](std::string &subsystem, std::string &msg,
413 std::unique_ptr<std::map<std::string, std::any>> &details) {
414 subsystem =
"OneItemBuffersFromHost";
415 msg = identifier() +
" write failed: buffer not ready";
416 details = std::make_unique<std::map<std::string, std::any>>();
417 (*details)[
"channel"] = identifier();
423 std::lock_guard<std::mutex> lock(bufferMutex);
424 void *bufferData = data_buffer->getPtr();
425 std::memcpy(bufferData,
data.getBytes(),
data.getSize());
426 data_buffer->flush();
429 completion_buffer->flush();
432 engine->mmio->write(BufferPtrOffset,
433 reinterpret_cast<uint64_t
>(data_buffer->getDevicePtr()));
436 reinterpret_cast<uint64_t
>(completion_buffer->getDevicePtr()));
439 [
this, data](std::string &subsystem, std::string &msg,
440 std::unique_ptr<std::map<std::string, std::any>> &details) {
441 subsystem =
"OneItemBuffersFromHost";
442 msg =
"initiated transfer of message";
443 details = std::make_unique<std::map<std::string, std::any>>();
444 (*details)[
"channel"] = identifier();
445 (*details)[
"data_size"] =
data.getSize();
446 (*details)[
"message_data"] =
data.toHex();
458 const std::string &channelName,
460 auto portIter =
ownedPorts.find(std::make_pair(idPath, channelName));
462 return *portIter->second;
463 std::unique_ptr<ChannelPort> port =
466 ownedPorts.emplace(std::make_pair(idPath, channelName), std::move(port));
473 for (
auto [channelName, dir, type] : bundleType->
getChannels()) {
478 ports.emplace(channelName, engineIter->second->requestPort(
479 idPath, channelName, dir, type));
486 auto [it, inserted] =
bundleEngineMap.try_emplace(channelName, engine);
488 throw std::runtime_error(
"Channel already exists in engine map");
496class EngineRegistry {
498 static std::map<std::string, registry::internal::EngineCreate> &get() {
499 static EngineRegistry instance;
500 return instance.engineRegistry;
504 std::map<std::string, registry::internal::EngineCreate> engineRegistry;
508std::unique_ptr<Engine>
510 const std::string &dmaEngineName,
AppIDPath idPath,
513 auto ® = EngineRegistry::get();
514 auto it = reg.find(dmaEngineName);
516 return std::make_unique<UnknownEngine>(conn, dmaEngineName);
517 return it->second(conn, idPath, details, clients);
522 auto tried = EngineRegistry::get().try_emplace(name, create);
524 throw std::runtime_error(
"Engine already exists in registry");
#define REGISTER_ENGINE(Name, TEngine)
Abstract class representing a connection to an accelerator.
ServiceClass * getService(AppIDPath id={}, std::string implName={}, ServiceImplDetails details={}, HWClientDetails clients={})
Get a typed reference to a particular service type.
Accelerator & getAccelerator()
Top level accelerator class.
std::string toStr() const
PortMap requestPorts(const AppIDPath &idPath, const BundleType *bundleType) const
Request ports for all the channels in a bundle.
std::map< std::string, Engine * > bundleEngineMap
void setEngine(const std::string &channelName, Engine *engine)
Set a particlar engine for a particular channel.
Services provide connections to 'bundles' – collections of named, unidirectional communication channe...
Bundles represent a collection of channels.
const ChannelVector & getChannels() const
Unidirectional channels are the basic communication primitive between the host and accelerator.
virtual bool pollImpl()
Method called by poll() to actually poll the channel if the channel is connected.
virtual void connectImpl(const ConnectOptions &options)
Called by all connect methods to let backends initiate the underlying connections.
Engines implement the actual channel communication between the host and the accelerator.
virtual void connect()
Start the engine, if applicable.
virtual ChannelPort & requestPort(AppIDPath idPath, const std::string &channelName, BundleType::Direction dir, const Type *type)
Get a port for a channel, from the cache if it exists or create it.
virtual std::unique_ptr< ChannelPort > createPort(AppIDPath idPath, const std::string &channelName, BundleType::Direction dir, const Type *type)=0
Each engine needs to know how to create a ports.
std::map< std::pair< AppIDPath, std::string >, std::unique_ptr< ChannelPort > > ownedPorts
BundlePort * resolvePort(const AppIDPath &path, AppIDPath &lastLookup) const
Attempt to resolve a path to a port.
void trace(const std::string &subsystem, const std::string &msg, const std::map< std::string, std::any > *details=nullptr)
Log a trace message.
A logical chunk of data representing serialized data.
A ChannelPort which reads data from the accelerator.
Root class of the ESI type system.
virtual std::ptrdiff_t getBitWidth() const
A ChannelPort which sends data to the accelerator.
A "slice" of some parent MMIO space.
connect(destination, source)
std::function< std::unique_ptr< Engine >(AcceleratorConnection &conn, AppIDPath idPath, const ServiceImplDetails &details, const HWClientDetails &clients)> EngineCreate
Engines can register themselves for pluggable functionality.
void registerEngine(const std::string &name, EngineCreate create)
std::unique_ptr< Engine > createEngine(AcceleratorConnection &conn, const std::string &dmaEngineName, AppIDPath idPath, const ServiceImplDetails &details, const HWClientDetails &clients)
Create an engine by name.
std::map< std::string, std::any > ServiceImplDetails
std::map< std::string, ChannelPort & > PortMap
std::vector< HWClientDetail > HWClientDetails