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(std::optional<unsigned>) {
194 buffer = engine->hostMem->allocate(bufferSize, {});
198bool OneItemBuffersToHostReadPort::pollImpl() {
200 uint8_t *bufferData =
reinterpret_cast<uint8_t *
>(buffer->getPtr());
201 if (bufferData[bufferSize - 1] == 0)
204 Logger &logger = engine->conn.getLogger();
210 [
this, data](std::string &subsystem, std::string &msg,
211 std::unique_ptr<std::map<std::string, std::any>> &details) {
212 subsystem =
"OneItemBuffersToHost";
213 msg =
"recieved message";
214 details = std::make_unique<std::map<std::string, std::any>>();
215 (*details)[
"channel"] = identifier();
216 (*details)[
"data_size"] =
data.getSize();
217 (*details)[
"message_data"] =
data.toHex();
219 if (callback(std::move(data))) {
222 [
this](std::string &subsystem, std::string &msg,
223 std::unique_ptr<std::map<std::string, std::any>> &details) {
224 subsystem =
"OneItemBuffersToHost";
225 msg =
"callback accepted data";
226 details = std::make_unique<std::map<std::string, std::any>>();
227 (*details)[
"channel"] = identifier();
232 [
this](std::string &subsystem, std::string &msg,
233 std::unique_ptr<std::map<std::string, std::any>> &details) {
234 subsystem =
"OneItemBuffersToHost";
235 msg =
"callback rejected data";
236 details = std::make_unique<std::map<std::string, std::any>>();
237 (*details)[
"channel"] = identifier();
265class OneItemBuffersFromHost;
269 static constexpr size_t BufferPtrOffset = 8;
270 static constexpr size_t CompletionPtrOffset = 16;
272 OneItemBuffersFromHostWritePort(
const Type *type,
273 OneItemBuffersFromHost *engine,
275 const std::string &channelName)
277 channelName(channelName) {
282 void connectImpl(std::optional<unsigned>)
override;
287 std::string identifier()
const {
return idPath.
toStr() +
"." + channelName; }
293 OneItemBuffersFromHost *engine;
295 std::unique_ptr<services::HostMem::HostMemRegion> data_buffer;
296 std::unique_ptr<services::HostMem::HostMemRegion> completion_buffer;
299 std::mutex bufferMutex;
303 std::string channelName;
306class OneItemBuffersFromHost :
public Engine {
307 friend class OneItemBuffersFromHostWritePort;
312 :
Engine(conn), thisPath(idPath) {
314 auto mmioIDIter = details.find(
"mmio");
315 if (mmioIDIter != details.end())
316 mmioID = std::any_cast<AppID>(mmioIDIter->second);
323 return std::make_unique<OneItemBuffersFromHost>(conn, idPath, details);
331 std::unique_ptr<ChannelPort> createPort(
AppIDPath idPath,
332 const std::string &channelName,
334 const Type *type)
override {
335 if (!BundlePort::isWrite(dir))
336 return std::make_unique<UnknownReadChannelPort>(
337 type, idPath.
toStr() +
"." + channelName +
338 " OneItemBuffersFromHost: cannot create read port");
339 return std::make_unique<OneItemBuffersFromHostWritePort>(type,
this, idPath,
345 std::optional<AppID> mmioID;
352void OneItemBuffersFromHost::connect() {
357 throw std::runtime_error(
"OneItemBuffersFromHost: no mmio path specified");
360 throw std::runtime_error(
"OneItemBuffersFromHost: no host memory service");
367 mmioPath.push_back(*mmioID);
371 throw std::runtime_error(
373 " OneItemBuffersFromHost: could not find MMIO port at " +
377 throw std::runtime_error(
379 " OneItemBuffersFromHost: MMIO port is not an MMIO port");
385void OneItemBuffersFromHostWritePort::connectImpl(std::optional<unsigned>) {
388 engine->hostMem->allocate(std::max(bufferSize, (
size_t)512), {});
389 completion_buffer = engine->hostMem->allocate(512, {});
392 *
static_cast<uint8_t *
>(completion_buffer->getPtr()) = 1;
395void OneItemBuffersFromHostWritePort::write(
const MessageData &data) {
396 while (!tryWrite(data))
398 std::this_thread::yield();
401bool OneItemBuffersFromHostWritePort::tryWrite(
const MessageData &data) {
402 Logger &logger = engine->conn.getLogger();
405 completion_buffer->flush();
406 uint8_t *completion =
407 reinterpret_cast<uint8_t *
>(completion_buffer->getPtr());
408 if (*completion == 0) {
410 [
this](std::string &subsystem, std::string &msg,
411 std::unique_ptr<std::map<std::string, std::any>> &details) {
412 subsystem =
"OneItemBuffersFromHost";
413 msg = identifier() +
" write failed: buffer not ready";
414 details = std::make_unique<std::map<std::string, std::any>>();
415 (*details)[
"channel"] = identifier();
421 std::lock_guard<std::mutex> lock(bufferMutex);
422 void *bufferData = data_buffer->getPtr();
423 std::memcpy(bufferData,
data.getBytes(),
data.getSize());
424 data_buffer->flush();
427 completion_buffer->flush();
430 engine->mmio->write(BufferPtrOffset,
431 reinterpret_cast<uint64_t
>(data_buffer->getDevicePtr()));
434 reinterpret_cast<uint64_t
>(completion_buffer->getDevicePtr()));
437 [
this, data](std::string &subsystem, std::string &msg,
438 std::unique_ptr<std::map<std::string, std::any>> &details) {
439 subsystem =
"OneItemBuffersFromHost";
440 msg =
"initiated transfer of message";
441 details = std::make_unique<std::map<std::string, std::any>>();
442 (*details)[
"channel"] = identifier();
443 (*details)[
"data_size"] =
data.getSize();
444 (*details)[
"message_data"] =
data.toHex();
456 const std::string &channelName,
458 auto portIter =
ownedPorts.find(std::make_pair(idPath, channelName));
460 return *portIter->second;
461 std::unique_ptr<ChannelPort> port =
464 ownedPorts.emplace(std::make_pair(idPath, channelName), std::move(port));
471 for (
auto [channelName, dir, type] : bundleType->
getChannels()) {
476 ports.emplace(channelName, engineIter->second->requestPort(
477 idPath, channelName, dir, type));
484 auto [it, inserted] =
bundleEngineMap.try_emplace(channelName, engine);
486 throw std::runtime_error(
"Channel already exists in engine map");
494class EngineRegistry {
496 static std::map<std::string, registry::internal::EngineCreate> &get() {
497 static EngineRegistry instance;
498 return instance.engineRegistry;
502 std::map<std::string, registry::internal::EngineCreate> engineRegistry;
506std::unique_ptr<Engine>
508 const std::string &dmaEngineName,
AppIDPath idPath,
511 auto ® = EngineRegistry::get();
512 auto it = reg.find(dmaEngineName);
514 return std::make_unique<UnknownEngine>(conn, dmaEngineName);
515 return it->second(conn, idPath, details, clients);
520 auto tried = EngineRegistry::get().try_emplace(name, create);
522 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 void connectImpl(std::optional< unsigned > bufferSize)
Called by all connect methods to let backends initiate the underlying connections.
virtual bool pollImpl()
Method called by poll() to actually poll the channel if the channel is connected.
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