32class UnknownEngine :
public Engine {
34 UnknownEngine(AcceleratorConnection &conn, std::string engineName)
35 : Engine(
conn), engineName(engineName) {}
38 throw std::runtime_error(
"Unknown engine '" + engineName +
"'");
41 std::unique_ptr<ChannelPort> createPort(AppIDPath idPath,
42 const std::string &channelName,
43 BundleType::Direction dir,
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;
70class OneItemBuffersToHost;
74 static constexpr size_t BufferPtrOffset = 8;
76 OneItemBuffersToHostReadPort(
const Type *type, OneItemBuffersToHost *engine,
77 AppIDPath idPath,
const std::string &channelName)
78 : ReadChannelPort(type), engine(engine), idPath(idPath),
79 channelName(channelName) {
90 void writeBufferPtr();
92 void connectImpl(
const ChannelPort::ConnectOptions &options)
override;
94 bool pollImpl()
override;
96 std::string identifier()
const {
return idPath.toStr() +
"." + channelName; }
104 std::unique_ptr<services::HostMem::HostMemRegion> buffer;
106 std::unique_ptr<SegmentedMessageData> pendingMessage;
108 uint64_t pollCount = 0;
112 std::string channelName;
115class OneItemBuffersToHost :
public Engine {
116 friend class OneItemBuffersToHostReadPort;
121 : Engine(
conn), thisPath(idPath) {
123 auto mmioIDIter = details.find(
"mmio");
124 if (mmioIDIter != details.end())
125 mmioID = std::any_cast<AppID>(mmioIDIter->second);
128 static std::unique_ptr<Engine> create(AcceleratorConnection &conn,
132 return std::make_unique<OneItemBuffersToHost>(conn, idPath, details);
140 std::unique_ptr<ChannelPort> createPort(AppIDPath idPath,
141 const std::string &channelName,
142 BundleType::Direction dir,
143 const Type *type)
override {
144 if (BundlePort::isWrite(dir))
145 return std::make_unique<UnknownWriteChannelPort>(
146 type, idPath.
toStr() +
"." + channelName +
147 " OneItemBuffersToHost: cannot create write port");
148 return std::make_unique<OneItemBuffersToHostReadPort>(type,
this, idPath,
154 std::optional<AppID> mmioID;
155 services::MMIO::MMIORegion *mmio;
156 services::HostMem *hostMem;
160void OneItemBuffersToHost::connect() {
165 throw std::runtime_error(
"OneItemBuffersToHost: no mmio path specified");
166 hostMem =
conn.getService<services::HostMem>();
168 throw std::runtime_error(
"OneItemBuffersToHost: no host memory service");
172 Accelerator &
acc =
conn.getAccelerator();
173 AppIDPath mmioPath = thisPath;
175 mmioPath.push_back(*mmioID);
177 BundlePort *port =
acc.resolvePort(mmioPath, lastPath);
179 throw std::runtime_error(
181 " OneItemBuffersToHost: could not find MMIO port at " +
183 mmio =
dynamic_cast<services::MMIO::MMIORegion *
>(port);
185 throw std::runtime_error(
187 " OneItemBuffersToHost: MMIO port is not an MMIO port");
193void OneItemBuffersToHostReadPort::writeBufferPtr() {
194 uint8_t *bufferData =
reinterpret_cast<uint8_t *
>(buffer->getPtr());
195 bufferData[bufferSize - 1] = 0;
197 engine->mmio->
write(BufferPtrOffset,
198 reinterpret_cast<uint64_t
>(buffer->getDevicePtr()));
201void OneItemBuffersToHostReadPort::connectImpl(
202 const ChannelPort::ConnectOptions &options) {
204 buffer = engine->hostMem->
allocate(bufferSize, {
true,
false});
206 throw std::runtime_error(
"OneItemBuffersToHost: failed to allocate " +
207 std::to_string(bufferSize) +
208 " bytes of host memory");
212bool OneItemBuffersToHostReadPort::pollImpl() {
213 Logger &logger = engine->conn.getLogger();
215 auto tryDeliverPending = [&]() {
217 [&](std::string &subsystem, std::string &msg,
218 std::unique_ptr<std::map<std::string, std::any>> &details) {
219 subsystem =
"OneItemBuffersToHost";
220 msg =
"received message";
221 details = std::make_unique<std::map<std::string, std::any>>();
222 MessageData flat = pendingMessage->toMessageData();
223 (*details)[
"channel"] = identifier();
224 (*details)[
"data_size"] = flat.getSize();
225 (*details)[
"message_data"] = flat.toHex();
228 if (!invokeCallback(pendingMessage)) {
230 [&](std::string &subsystem, std::string &msg,
231 std::unique_ptr<std::map<std::string, std::any>> &details) {
232 subsystem =
"OneItemBuffersToHost";
233 msg =
"callback rejected data";
234 details = std::make_unique<std::map<std::string, std::any>>();
235 (*details)[
"channel"] = identifier();
240 pendingMessage.reset();
243 [
this](std::string &subsystem, std::string &msg,
244 std::unique_ptr<std::map<std::string, std::any>> &details) {
245 subsystem =
"OneItemBuffersToHost";
246 msg =
"callback accepted data";
247 details = std::make_unique<std::map<std::string, std::any>>();
248 (*details)[
"channel"] = identifier();
254 return tryDeliverPending();
257 uint8_t *bufferData =
reinterpret_cast<uint8_t *
>(buffer->getPtr());
258 if (bufferData[bufferSize - 1] == 0)
262 pendingMessage = std::make_unique<MessageData>(bufferData, bufferSize - 1);
263 return tryDeliverPending();
289class OneItemBuffersFromHost;
293 static constexpr size_t BufferPtrOffset = 8;
294 static constexpr size_t CompletionPtrOffset = 16;
296 OneItemBuffersFromHostWritePort(
const Type *type,
297 OneItemBuffersFromHost *engine,
299 const std::string &channelName)
301 channelName(channelName) {}
304 void connectImpl(
const ChannelPort::ConnectOptions &options)
override;
306 std::string identifier()
const {
return idPath.
toStr() +
"." + channelName; }
309 void writeImpl(
const MessageData &)
override;
310 bool tryWriteImpl(
const MessageData &data)
override;
313 void enqueueFrames(
const MessageData &data);
316 bool pollImpl()
override;
321 std::unique_ptr<services::HostMem::HostMemRegion> data_buffer;
322 std::unique_ptr<services::HostMem::HostMemRegion> completion_buffer;
325 std::mutex bufferMutex;
330 std::queue<MessageData> pendingFrames;
334 std::string channelName;
338 friend class OneItemBuffersFromHostWritePort;
343 : Engine(
conn), thisPath(idPath) {
345 auto mmioIDIter = details.find(
"mmio");
346 if (mmioIDIter != details.end())
347 mmioID = std::any_cast<AppID>(mmioIDIter->second);
350 static std::unique_ptr<Engine> create(AcceleratorConnection &conn,
354 return std::make_unique<OneItemBuffersFromHost>(conn, idPath, details);
362 std::unique_ptr<ChannelPort> createPort(AppIDPath idPath,
363 const std::string &channelName,
364 BundleType::Direction dir,
365 const Type *type)
override {
366 if (!BundlePort::isWrite(dir))
367 return std::make_unique<UnknownReadChannelPort>(
368 type, idPath.
toStr() +
"." + channelName +
369 " OneItemBuffersFromHost: cannot create read port");
370 return std::make_unique<OneItemBuffersFromHostWritePort>(type,
this, idPath,
376 std::optional<AppID> mmioID;
377 services::MMIO::MMIORegion *mmio;
378 services::HostMem *hostMem;
383void OneItemBuffersFromHost::connect() {
388 throw std::runtime_error(
"OneItemBuffersFromHost: no mmio path specified");
389 hostMem =
conn.getService<services::HostMem>();
391 throw std::runtime_error(
"OneItemBuffersFromHost: no host memory service");
395 Accelerator &
acc =
conn.getAccelerator();
396 AppIDPath mmioPath = thisPath;
398 mmioPath.push_back(*mmioID);
400 BundlePort *port =
acc.resolvePort(mmioPath, lastPath);
402 throw std::runtime_error(
404 " OneItemBuffersFromHost: could not find MMIO port at " +
406 mmio =
dynamic_cast<services::MMIO::MMIORegion *
>(port);
408 throw std::runtime_error(
410 " OneItemBuffersFromHost: MMIO port is not an MMIO port");
416void OneItemBuffersFromHostWritePort::connectImpl(
417 const ChannelPort::ConnectOptions &options) {
419 data_buffer = engine->hostMem->
allocate(
420 std::max(getFrameSizeBytes(), (
size_t)512), {
false,
false});
421 completion_buffer = engine->hostMem->
allocate(512, {
true,
false});
422 if (!data_buffer || !completion_buffer)
423 throw std::runtime_error(
424 "OneItemBuffersFromHost: failed to allocate host memory");
427 *
static_cast<uint8_t *
>(completion_buffer->getPtr()) = 1;
430void OneItemBuffersFromHostWritePort::enqueueFrames(
const MessageData &data) {
431 auto frames = getMessageFrames(data);
432 std::lock_guard<std::mutex> lock(bufferMutex);
433 for (
const auto &frame : frames)
434 pendingFrames.push(frame);
437void OneItemBuffersFromHostWritePort::writeImpl(
const MessageData &data) {
438 while (!tryWriteImpl(data))
440 std::this_thread::yield();
443bool OneItemBuffersFromHostWritePort::tryWriteImpl(
const MessageData &data) {
446 std::lock_guard<std::mutex> lock(bufferMutex);
447 if (!pendingFrames.empty())
455bool OneItemBuffersFromHostWritePort::pollImpl() {
456 Logger &logger = engine->conn.getLogger();
459 completion_buffer->flush();
460 uint8_t *completion =
461 reinterpret_cast<uint8_t *
>(completion_buffer->getPtr());
462 if (*completion == 0)
465 std::lock_guard<std::mutex> lock(bufferMutex);
470 if (pendingFrames.empty())
473 MessageData frame = std::move(pendingFrames.front());
478 void *bufferData = data_buffer->getPtr();
479 std::memcpy(bufferData, src, sendSize);
480 data_buffer->flush();
482 completion_buffer->flush();
483 engine->mmio->
write(BufferPtrOffset,
484 reinterpret_cast<uint64_t
>(data_buffer->getDevicePtr()));
487 reinterpret_cast<uint64_t
>(completion_buffer->getDevicePtr()));
489 logger.
trace([
this, sendSize](
490 std::string &subsystem, std::string &msg,
491 std::unique_ptr<std::map<std::string, std::any>> &details) {
492 subsystem =
"OneItemBuffersFromHost";
493 msg =
"initiated transfer";
494 details = std::make_unique<std::map<std::string, std::any>>();
495 (*details)[
"channel"] = identifier();
496 (*details)[
"data_size"] = sendSize;
511 const std::string &channelName,
513 auto portIter =
ownedPorts.find(std::make_pair(idPath, channelName));
515 return *portIter->second;
516 std::unique_ptr<ChannelPort> port =
519 ownedPorts.emplace(std::make_pair(idPath, channelName), std::move(port));
526 for (
auto [channelName, dir, type] : bundleType->
getChannels()) {
531 ports.emplace(channelName, engineIter->second->requestPort(
532 idPath, channelName, dir, type));
539 auto [it, inserted] =
bundleEngineMap.try_emplace(channelName, engine);
541 throw std::runtime_error(
"Channel already exists in engine map");
549class EngineRegistry {
551 static std::map<std::string, registry::internal::EngineCreate> &get() {
552 static EngineRegistry instance;
553 return instance.engineRegistry;
557 std::map<std::string, registry::internal::EngineCreate> engineRegistry;
561std::unique_ptr<Engine>
563 const std::string &dmaEngineName,
AppIDPath idPath,
566 auto ® = EngineRegistry::get();
567 auto it = reg.find(dmaEngineName);
569 return std::make_unique<UnknownEngine>(conn, dmaEngineName);
570 return it->second(conn, idPath, details, clients);
575 auto tried = EngineRegistry::get().try_emplace(name, create);
577 throw std::runtime_error(
"Engine already exists in registry");
#define REGISTER_ENGINE(Name, TEngine)
Abstract class representing a connection to an accelerator.
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.
Bundles represent a collection of channels.
const ChannelVector & getChannels() const
Unidirectional channels are the basic communication primitive between the host and accelerator.
Engines implement the actual channel communication between the host and the accelerator.
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.
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.
virtual void start()
In cases where necessary, enable host memory services.
virtual std::unique_ptr< HostMemRegion > allocate(std::size_t size, Options opts) const override=0
Allocate a region of host memory in accelerator accessible address space.
virtual void write(uint32_t addr, uint64_t data)
Write a 64-bit value to this region, not the global address 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, ChannelPort & > PortMap
std::vector< HWClientDetail > HWClientDetails
std::map< std::string, std::any > ServiceImplDetails
OneItemBuffersFromHost(Type client_type)
OneItemBuffersToHost(Type client_type)