32class UnknownEngine :
public Engine {
38 throw std::runtime_error(
"Unknown engine '" + engineName +
"'");
42 const std::string &channelName,
44 const Type *type)
override {
45 if (BundlePort::isWrite(dir))
46 return std::make_unique<UnknownWriteChannelPort>(
48 "Unknown engine '" + engineName +
"': cannot create write port");
50 return std::make_unique<UnknownReadChannelPort>(
51 type,
"Unknown engine '" + engineName +
"': cannot create read port");
55 std::string engineName;
74 static constexpr size_t BufferPtrOffset = 8;
76 OneItemBuffersToHostReadPort(
const Type *type, OneItemBuffersToHost *engine,
77 AppIDPath idPath,
const std::string &channelName)
79 channelName(channelName) {
84 void writeBufferPtr();
90 std::string identifier()
const {
return idPath.
toStr() +
"." + channelName; }
98 std::unique_ptr<services::HostMem::HostMemRegion> buffer;
100 uint64_t pollCount = 0;
104 std::string channelName;
108 friend class OneItemBuffersToHostReadPort;
115 auto mmioIDIter = details.find(
"mmio");
116 if (mmioIDIter != details.end())
117 mmioID = std::any_cast<AppID>(mmioIDIter->second);
124 return std::make_unique<OneItemBuffersToHost>(conn, idPath, details);
133 const std::string &channelName,
135 const Type *type)
override {
136 if (BundlePort::isWrite(dir))
137 return std::make_unique<UnknownWriteChannelPort>(
138 type, idPath.
toStr() +
"." + channelName +
139 " OneItemBuffersToHost: cannot create write port");
140 return std::make_unique<OneItemBuffersToHostReadPort>(type,
this, idPath,
146 std::optional<AppID> mmioID;
152void OneItemBuffersToHost::connect() {
157 throw std::runtime_error(
"OneItemBuffersToHost: no mmio path specified");
160 throw std::runtime_error(
"OneItemBuffersToHost: no host memory service");
167 mmioPath.push_back(*mmioID);
171 throw std::runtime_error(
173 " OneItemBuffersToHost: could not find MMIO port at " +
177 throw std::runtime_error(
179 " OneItemBuffersToHost: MMIO port is not an MMIO port");
185void OneItemBuffersToHostReadPort::writeBufferPtr() {
186 uint8_t *bufferData =
reinterpret_cast<uint8_t *
>(buffer->getPtr());
187 bufferData[bufferSize - 1] = 0;
189 engine->mmio->write(BufferPtrOffset,
190 reinterpret_cast<uint64_t
>(buffer->getDevicePtr()));
193void OneItemBuffersToHostReadPort::connectImpl(
196 buffer = engine->hostMem->allocate(bufferSize, {
true,
false});
200bool OneItemBuffersToHostReadPort::pollImpl() {
202 uint8_t *bufferData =
reinterpret_cast<uint8_t *
>(buffer->getPtr());
203 if (bufferData[bufferSize - 1] == 0)
206 Logger &logger = engine->conn.getLogger();
212 [
this, data](std::string &subsystem, std::string &msg,
213 std::unique_ptr<std::map<std::string, std::any>> &details) {
214 subsystem =
"OneItemBuffersToHost";
215 msg =
"recieved message";
216 details = std::make_unique<std::map<std::string, std::any>>();
217 (*details)[
"channel"] = identifier();
218 (*details)[
"data_size"] =
data.getSize();
219 (*details)[
"message_data"] =
data.toHex();
221 if (callback(std::move(data))) {
224 [
this](std::string &subsystem, std::string &msg,
225 std::unique_ptr<std::map<std::string, std::any>> &details) {
226 subsystem =
"OneItemBuffersToHost";
227 msg =
"callback accepted data";
228 details = std::make_unique<std::map<std::string, std::any>>();
229 (*details)[
"channel"] = identifier();
234 [
this](std::string &subsystem, std::string &msg,
235 std::unique_ptr<std::map<std::string, std::any>> &details) {
236 subsystem =
"OneItemBuffersToHost";
237 msg =
"callback rejected data";
238 details = std::make_unique<std::map<std::string, std::any>>();
239 (*details)[
"channel"] = identifier();
267class OneItemBuffersFromHost;
271 static constexpr size_t BufferPtrOffset = 8;
272 static constexpr size_t CompletionPtrOffset = 16;
274 OneItemBuffersFromHostWritePort(
const Type *type,
275 OneItemBuffersFromHost *engine,
277 const std::string &channelName)
279 channelName(channelName) {
286 std::string identifier()
const {
return idPath.
toStr() +
"." + channelName; }
290 bool tryWriteImpl(
const MessageData &data)
override;
296 bool pollImpl()
override;
299 size_t frameSizeBytes;
303 std::unique_ptr<services::HostMem::HostMemRegion> data_buffer;
304 std::unique_ptr<services::HostMem::HostMemRegion> completion_buffer;
307 std::mutex bufferMutex;
311 std::queue<MessageData> pendingFrames;
315 std::string channelName;
319 friend class OneItemBuffersFromHostWritePort;
326 auto mmioIDIter = details.find(
"mmio");
327 if (mmioIDIter != details.end())
328 mmioID = std::any_cast<AppID>(mmioIDIter->second);
335 return std::make_unique<OneItemBuffersFromHost>(conn, idPath, details);
343 std::unique_ptr<ChannelPort> createPort(
AppIDPath idPath,
344 const std::string &channelName,
346 const Type *type)
override {
347 if (!BundlePort::isWrite(dir))
348 return std::make_unique<UnknownReadChannelPort>(
349 type, idPath.
toStr() +
"." + channelName +
350 " OneItemBuffersFromHost: cannot create read port");
351 return std::make_unique<OneItemBuffersFromHostWritePort>(type,
this, idPath,
357 std::optional<AppID> mmioID;
364void OneItemBuffersFromHost::connect() {
369 throw std::runtime_error(
"OneItemBuffersFromHost: no mmio path specified");
372 throw std::runtime_error(
"OneItemBuffersFromHost: no host memory service");
379 mmioPath.push_back(*mmioID);
383 throw std::runtime_error(
385 " OneItemBuffersFromHost: could not find MMIO port at " +
389 throw std::runtime_error(
391 " OneItemBuffersFromHost: MMIO port is not an MMIO port");
397void OneItemBuffersFromHostWritePort::connectImpl(
400 data_buffer = engine->hostMem->allocate(std::max(frameSizeBytes, (
size_t)512),
402 completion_buffer = engine->hostMem->allocate(512, {
true,
false});
405 *
static_cast<uint8_t *
>(completion_buffer->getPtr()) = 1;
408void OneItemBuffersFromHostWritePort::enqueueFrames(
const MessageData &data) {
409 auto frames = getMessageFrames(data);
410 std::lock_guard<std::mutex> lock(bufferMutex);
411 for (
const auto &frame : frames)
412 pendingFrames.push(frame);
415void OneItemBuffersFromHostWritePort::writeImpl(
const MessageData &data) {
416 while (!tryWriteImpl(data))
418 std::this_thread::yield();
421bool OneItemBuffersFromHostWritePort::tryWriteImpl(
const MessageData &data) {
424 std::lock_guard<std::mutex> lock(bufferMutex);
425 if (!pendingFrames.empty())
433bool OneItemBuffersFromHostWritePort::pollImpl() {
434 Logger &logger = engine->conn.getLogger();
437 completion_buffer->flush();
438 uint8_t *completion =
439 reinterpret_cast<uint8_t *
>(completion_buffer->getPtr());
440 if (*completion == 0)
443 std::lock_guard<std::mutex> lock(bufferMutex);
448 if (pendingFrames.empty())
451 MessageData frame = std::move(pendingFrames.front());
456 void *bufferData = data_buffer->getPtr();
457 std::memcpy(bufferData, src, sendSize);
458 data_buffer->flush();
460 completion_buffer->flush();
461 engine->mmio->write(BufferPtrOffset,
462 reinterpret_cast<uint64_t
>(data_buffer->getDevicePtr()));
465 reinterpret_cast<uint64_t
>(completion_buffer->getDevicePtr()));
467 logger.
trace([
this, sendSize](
468 std::string &subsystem, std::string &msg,
469 std::unique_ptr<std::map<std::string, std::any>> &details) {
470 subsystem =
"OneItemBuffersFromHost";
471 msg =
"initiated transfer";
472 details = std::make_unique<std::map<std::string, std::any>>();
473 (*details)[
"channel"] = identifier();
474 (*details)[
"data_size"] = sendSize;
489 const std::string &channelName,
491 auto portIter =
ownedPorts.find(std::make_pair(idPath, channelName));
493 return *portIter->second;
494 std::unique_ptr<ChannelPort> port =
497 ownedPorts.emplace(std::make_pair(idPath, channelName), std::move(port));
504 for (
auto [channelName, dir, type] : bundleType->
getChannels()) {
509 ports.emplace(channelName, engineIter->second->requestPort(
510 idPath, channelName, dir, type));
517 auto [it, inserted] =
bundleEngineMap.try_emplace(channelName, engine);
519 throw std::runtime_error(
"Channel already exists in engine map");
527class EngineRegistry {
529 static std::map<std::string, registry::internal::EngineCreate> &get() {
530 static EngineRegistry instance;
531 return instance.engineRegistry;
535 std::map<std::string, registry::internal::EngineCreate> engineRegistry;
539std::unique_ptr<Engine>
541 const std::string &dmaEngineName,
AppIDPath idPath,
544 auto ® = EngineRegistry::get();
545 auto it = reg.find(dmaEngineName);
547 return std::make_unique<UnknownEngine>(conn, dmaEngineName);
548 return it->second(conn, idPath, details, clients);
553 auto tried = EngineRegistry::get().try_emplace(name, create);
555 throw std::runtime_error(
"Engine already exists in registry");
#define REGISTER_ENGINE(Name, TEngine)
Abstract class representing a connection to an accelerator.
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
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.
const uint8_t * getBytes() const
size_t getSize() const
Get the size of the data in bytes.
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.
uint64_t bitsToBytes(uint64_t bits)
Compute ceil(bits/8).
std::map< std::string, std::any > ServiceImplDetails
std::map< std::string, ChannelPort & > PortMap
std::vector< HWClientDetail > HWClientDetails
OneItemBuffersFromHost(Type client_type)
OneItemBuffersToHost(Type client_type)