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)
207 engine->conn.getLogger().debug(
208 [
this, data](std::string &subsystem, std::string &msg,
209 std::unique_ptr<std::map<std::string, std::any>> &details) {
210 subsystem =
"OneItemBuffersToHost";
211 msg = identifier() +
" got message contents 0x" +
data.toHex();
213 if (callback(std::move(data))) {
243class OneItemBuffersFromHost;
247 static constexpr size_t BufferPtrOffset = 8;
248 static constexpr size_t CompletionPtrOffset = 16;
250 OneItemBuffersFromHostWritePort(
const Type *type,
251 OneItemBuffersFromHost *engine,
253 const std::string &channelName)
255 channelName(channelName) {
260 void connectImpl(std::optional<unsigned>)
override;
265 std::string identifier()
const {
return idPath.
toStr() +
"." + channelName; }
271 OneItemBuffersFromHost *engine;
273 std::unique_ptr<services::HostMem::HostMemRegion> data_buffer;
274 std::unique_ptr<services::HostMem::HostMemRegion> completion_buffer;
277 std::mutex bufferMutex;
281 std::string channelName;
284class OneItemBuffersFromHost :
public Engine {
285 friend class OneItemBuffersFromHostWritePort;
290 :
Engine(conn), thisPath(idPath) {
292 auto mmioIDIter = details.find(
"mmio");
293 if (mmioIDIter != details.end())
294 mmioID = std::any_cast<AppID>(mmioIDIter->second);
301 return std::make_unique<OneItemBuffersFromHost>(conn, idPath, details);
309 std::unique_ptr<ChannelPort> createPort(
AppIDPath idPath,
310 const std::string &channelName,
312 const Type *type)
override {
313 if (!BundlePort::isWrite(dir))
314 return std::make_unique<UnknownReadChannelPort>(
315 type, idPath.
toStr() +
"." + channelName +
316 " OneItemBuffersFromHost: cannot create read port");
317 return std::make_unique<OneItemBuffersFromHostWritePort>(type,
this, idPath,
323 std::optional<AppID> mmioID;
330void OneItemBuffersFromHost::connect() {
335 throw std::runtime_error(
"OneItemBuffersFromHost: no mmio path specified");
338 throw std::runtime_error(
"OneItemBuffersFromHost: no host memory service");
345 mmioPath.push_back(*mmioID);
349 throw std::runtime_error(
351 " OneItemBuffersFromHost: could not find MMIO port at " +
355 throw std::runtime_error(
357 " OneItemBuffersFromHost: MMIO port is not an MMIO port");
363void OneItemBuffersFromHostWritePort::connectImpl(std::optional<unsigned>) {
366 engine->hostMem->allocate(std::max(bufferSize, (
size_t)512), {});
367 completion_buffer = engine->hostMem->allocate(512, {});
370 *
static_cast<uint8_t *
>(completion_buffer->getPtr()) = 1;
373void OneItemBuffersFromHostWritePort::write(
const MessageData &data) {
374 while (!tryWrite(data))
376 std::this_thread::yield();
379bool OneItemBuffersFromHostWritePort::tryWrite(
const MessageData &data) {
381 completion_buffer->flush();
382 uint8_t *completion =
383 reinterpret_cast<uint8_t *
>(completion_buffer->getPtr());
384 if (*completion == 0)
388 std::lock_guard<std::mutex> lock(bufferMutex);
389 void *bufferData = data_buffer->getPtr();
390 std::memcpy(bufferData,
data.getBytes(),
data.getSize());
391 data_buffer->flush();
394 completion_buffer->flush();
397 engine->mmio->write(BufferPtrOffset,
398 reinterpret_cast<uint64_t
>(data_buffer->getDevicePtr()));
401 reinterpret_cast<uint64_t
>(completion_buffer->getDevicePtr()));
412 const std::string &channelName,
414 auto portIter =
ownedPorts.find(std::make_pair(idPath, channelName));
416 return *portIter->second;
417 std::unique_ptr<ChannelPort> port =
420 ownedPorts.emplace(std::make_pair(idPath, channelName), std::move(port));
427 for (
auto [channelName, dir, type] : bundleType->
getChannels()) {
432 ports.emplace(channelName, engineIter->second->requestPort(
433 idPath, channelName, dir, type));
440 auto [it, inserted] =
bundleEngineMap.try_emplace(channelName, engine);
442 throw std::runtime_error(
"Channel already exists in engine map");
450class EngineRegistry {
452 static std::map<std::string, registry::internal::EngineCreate> &get() {
453 static EngineRegistry instance;
454 return instance.engineRegistry;
458 std::map<std::string, registry::internal::EngineCreate> engineRegistry;
462std::unique_ptr<Engine>
464 const std::string &dmaEngineName,
AppIDPath idPath,
467 auto ® = EngineRegistry::get();
468 auto it = reg.find(dmaEngineName);
470 return std::make_unique<UnknownEngine>(conn, dmaEngineName);
471 return it->second(conn, idPath, details, clients);
476 auto tried = EngineRegistry::get().try_emplace(name, create);
478 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.
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