22#ifndef ESI_TYPED_PORTS_H
23#define ESI_TYPED_PORTS_H
55 using std::runtime_error::runtime_error;
73 return {(bv->getWidth() + 7) / 8, bv->getWidth()};
81 if constexpr (std::is_integral_v<T> && !std::is_same_v<T, bool>) {
83 std::vector<uint8_t> buf(wi.
bytes, 0);
84 std::memcpy(buf.data(), &data, std::min(wi.
bytes,
sizeof(T)));
87 }
else if constexpr (std::is_base_of_v<SegmentedMessageData, T>) {
88 return data.toMessageData();
90 return MessageData(
reinterpret_cast<const uint8_t *
>(&data),
sizeof(T));
104 if constexpr (std::is_integral_v<T> && !std::is_same_v<T, bool>) {
109 std::memcpy(&val, msg.
getBytes(), std::min(wi.
bytes,
sizeof(T)));
111 if constexpr (std::is_signed_v<T>) {
113 size_t signByte = signBit / 8;
114 uint8_t signMask = uint8_t(1) << (signBit % 8);
115 if (signByte < wi.
bytes && (msg.
getBytes()[signByte] & signMask)) {
118 val |=
static_cast<T
>(~T(0)) << wi.
bitWidth;
141template <
typename T,
typename =
void>
154 if (
auto *flat =
dynamic_cast<const MessageData *
>(&msg))
174 bool push(std::unique_ptr<SegmentedMessageData> &msg) {
175 std::scoped_lock<std::mutex> lock(
mutex);
177 throw std::runtime_error(
"PODTypeDeserializer::push: null message");
195 std::scoped_lock<std::mutex> lock(
mutex);
214template <
typename T,
typename =
void>
221 using type =
typename T::TypeDeserializer;
254 bool push(std::unique_ptr<SegmentedMessageData> &msg) {
255 std::scoped_lock<std::mutex> lock(
mutex);
257 throw std::runtime_error(
258 "QueuedDecodeTypeDeserializer::push: null message");
265 throw std::runtime_error(
266 "QueuedDecodeTypeDeserializer::push: decode must consume the "
268 for (std::unique_ptr<T> &value : decoded) {
270 throw std::runtime_error(
271 "QueuedDecodeTypeDeserializer::push: null decoded output");
285 std::scoped_lock<std::mutex> lock(
mutex);
301 throw std::runtime_error(
302 "QueuedDecodeTypeDeserializer::poke: null pending output");
348 "header and data frames must be the same width");
355 const uint8_t *bytes = flat.
getBytes();
359 while (offset < size) {
361 size_t chunk = std::min(needed, size - offset);
375 count_type batchCount = T::_headerCount(frame);
376 if (batchCount == 0) {
380 throw std::runtime_error(
381 "SerialListTypeDeserializer: footer received before any "
417template <
typename T,
typename =
void>
422 : std::is_convertible<decltype(T::_ESI_ID), std::string_view> {};
429template <
typename T,
typename =
void>
434 : std::is_convertible<decltype(T::_ESI_WINDOW_ID), std::string_view> {};
450template <
typename T, std::
size_t N>
465 if (
auto *windowType =
dynamic_cast<const WindowType *
>(portType)) {
467 if (std::string_view(windowType->getID()) != T::_ESI_WINDOW_ID)
469 "ESI window mismatch: C++ type has _ESI_WINDOW_ID '" +
470 std::string(T::_ESI_WINDOW_ID) +
"' but port window type is '" +
471 windowType->toString(
true) +
"'");
474 "ESI type mismatch: port is a window type ('" +
475 windowType->toString(
true) +
476 "') but C++ type has no _ESI_WINDOW_ID");
481 std::string(
"ESI type mismatch: C++ type has _ESI_WINDOW_ID '") +
482 std::string(T::_ESI_WINDOW_ID) +
"' but port type is not a window: '" +
488 if (std::string_view(portType->
getID()) != T::_ESI_ID)
490 "ESI type mismatch: C++ type has _ESI_ID '" +
491 std::string(T::_ESI_ID) +
"' but port type is '" +
493 }
else if constexpr (std::is_void_v<T>) {
494 if (!
dynamic_cast<const VoidType *
>(portType))
496 "void, but port type is '" +
499 }
else if constexpr (std::is_same_v<T, bool>) {
501 auto *bits =
dynamic_cast<const BitsType *
>(portType);
502 if (!bits || bits->getWidth() > 1)
504 "ESI type mismatch: expected BitsType with width <= 1 for "
505 "bool, but port type is '" +
512 using Element =
typename T::value_type;
513 constexpr size_t N = std::tuple_size<T>::value;
514 auto *arr =
dynamic_cast<const ArrayType *
>(portType);
517 "ESI type mismatch: expected ArrayType for std::array, but port "
520 if (arr->getSize() != N)
522 std::to_string(arr->getSize()) +
523 " does not match std::array size " +
526 }
else if constexpr (std::is_integral_v<T> && std::is_signed_v<T>) {
527 auto *sint =
dynamic_cast<const SIntType *
>(portType);
530 "ESI type mismatch: expected SIntType for signed integer, "
531 "but port type is '" +
533 if (sint->getWidth() >
sizeof(T) * 8)
535 "ESI type mismatch: SIntType width " +
536 std::to_string(sint->getWidth()) +
" does not fit in " +
537 std::to_string(
sizeof(T) * 8) +
"-bit signed integer");
539 if (
sizeof(T) > 1 && sint->getWidth() <= (
sizeof(T) / 2) * 8)
541 std::to_string(sint->getWidth()) +
542 " should use a smaller C++ type than " +
543 std::to_string(
sizeof(T) * 8) +
"-bit");
544 }
else if constexpr (std::is_integral_v<T> && std::is_unsigned_v<T>) {
546 auto *uintPort =
dynamic_cast<const UIntType *
>(portType);
547 auto *bits =
dynamic_cast<const BitsType *
>(portType);
548 if (!uintPort && !bits)
550 "ESI type mismatch: expected UIntType or BitsType for unsigned "
551 "integer, but port type is '" +
553 uint64_t width = uintPort ? uintPort->getWidth() : bits->getWidth();
554 if (width >
sizeof(T) * 8)
556 "ESI type mismatch: bit width " + std::to_string(width) +
557 " does not fit in " + std::to_string(
sizeof(T) * 8) +
558 "-bit unsigned integer");
560 if (
sizeof(T) > 1 && width <= (
sizeof(T) / 2) * 8)
562 std::to_string(width) +
563 " should use a smaller C++ type than " +
564 std::to_string(
sizeof(T) * 8) +
"-bit");
567 std::string(
"Cannot verify type compatibility for C++ type '") +
568 typeid(T).name() +
"' against ESI port type '" +
586 std::string(
"ESI type mismatch: C++ type has _ESI_WINDOW_ID '") +
587 std::string(T::_ESI_WINDOW_ID) +
588 "' but port is not a window type ('" +
590 : std::string(
"<null>")) +
595 "ESI type mismatch: port is a window type ('" +
597 "') but C++ type has no _ESI_WINDOW_ID");
599 const Type *forwardType =
600 windowType ?
static_cast<const Type *
>(windowType) : port->
getType();
612template <
typename T,
bool SkipTypeCheck = false>
633 void write(std::unique_ptr<T> &data) {
635 throw std::runtime_error(
"TypedWritePort::write: null unique_ptr");
636 if constexpr (std::is_base_of_v<SegmentedMessageData, T>) {
637 inner->write(std::move(data));
654 throw std::runtime_error(
"TypedWritePort::tryWrite: null unique_ptr");
655 if constexpr (std::is_base_of_v<SegmentedMessageData, T>) {
656 std::unique_ptr<SegmentedMessageData> msg(data.release());
657 bool written =
inner->tryWrite(msg);
660 data.reset(
static_cast<T *
>(msg.release()));
732template <
typename T,
bool SkipTypeCheck = false>
763 [
this](std::unique_ptr<SegmentedMessageData> &msg) ->
bool {
779 void connect(std::function<
bool(
const T &)> callback,
782 connect([cb = std::move(callback)](
783 std::unique_ptr<T> &value) ->
bool {
return cb(*value); },
801 [
this](std::unique_ptr<SegmentedMessageData> &msg) ->
bool {
817 std::future<std::unique_ptr<T>> f =
readAsync();
827 throw std::runtime_error(
828 "Cannot read from a callback channel. `connect()` without a "
829 "callback specified to use polling mode.");
831 throw std::runtime_error(
832 "Cannot read from a disconnected channel. `connect()` first.");
835 throw std::runtime_error(
836 "Cannot read from a disconnected channel. `connect()` first.");
838 std::future<std::unique_ptr<T>> future =
pollingState->readAsync();
884 throw std::runtime_error(
"Channel already connected");
885 if constexpr (SkipTypeCheck) {
938 [cb = std::move(callback)](
MessageData) ->
bool {
return cb(); }, opts);
943 inner->read(outData);
947 auto innerFuture =
inner->readAsync();
949 std::launch::deferred,
950 [f = std::move(innerFuture)]()
mutable ->
void { f.get(); });
999 return std::async(std::launch::deferred,
1000 [fut = std::move(inner)]()
mutable -> T {
1001 std::unique_ptr<T> v = fut.get();
1003 throw std::runtime_error(
1004 "TypedFunction: deserializer produced a null "
1006 return std::move(*v);
1014 "TypedFunction: null Function pointer (getAs failed or wrong type)");
1019 throw std::runtime_error(
"TypedFunction: must be 'connect'ed before "
1025 throw std::runtime_error(
"TypedFunction is already connected");
1030template <
typename ArgT,
typename ResultT,
bool SkipTypeCheck = false>
1051 std::future<ResultT>
call(
const ArgT &arg) {
1059 std::scoped_lock<std::mutex> lock(
callMutex);
1068 typename First,
typename... Rest,
1069 typename = std::enable_if_t<
1070 std::is_constructible_v<ArgT, First, Rest...> &&
1071 (!std::is_same_v<std::decay_t<First>, ArgT> ||
sizeof...(Rest) != 0)>>
1072 std::future<ResultT>
call(First &&first, Rest &&...rest) {
1073 return call(ArgT(std::forward<First>(first), std::forward<Rest>(rest)...));
1077 template <
typename... Args>
1079 ->
decltype(this->
call(std::forward<Args>(args)...)) {
1080 return call(std::forward<Args>(args)...);
1088 std::optional<TypedWritePort<ArgT, SkipTypeCheck>>
argPort;
1094template <
typename ResultT,
bool SkipTypeCheck>
1117 std::scoped_lock<std::mutex> lock(
callMutex);
1136template <
typename ArgT,
bool SkipTypeCheck>
1156 std::future<void>
call(
const ArgT &arg) {
1159 std::scoped_lock<std::mutex> lock(
callMutex);
1168 typename First,
typename... Rest,
1169 typename = std::enable_if_t<
1170 std::is_constructible_v<ArgT, First, Rest...> &&
1171 (!std::is_same_v<std::decay_t<First>, ArgT> ||
sizeof...(Rest) != 0)>>
1172 std::future<void>
call(First &&first, Rest &&...rest) {
1173 return call(ArgT(std::forward<First>(first), std::forward<Rest>(rest)...));
1177 template <
typename... Args>
1179 ->
decltype(this->
call(std::forward<Args>(args)...)) {
1180 return call(std::forward<Args>(args)...);
1188 std::optional<TypedWritePort<ArgT, SkipTypeCheck>>
argPort;
1194template <
bool SkipTypeCheck>
1217 std::scoped_lock<std::mutex> lock(
callMutex);
1273 "TypedCallback: null Callback pointer (getAs failed or wrong type)");
1279 throw std::runtime_error(
"TypedCallback is already connected");
1284template <
typename ArgT,
typename ResultT,
bool SkipTypeCheck = false>
1292 void connect(std::function<ResultT(
const ArgT &)> callback,
1293 bool quick =
false) {
1308 [
this](
const ArgT &arg) ->
bool {
1321 std::optional<TypedReadPort<ArgT, SkipTypeCheck>>
argPort;
1322 std::optional<TypedWritePort<ResultT, SkipTypeCheck>>
resultPort;
1327template <
typename ResultT,
bool SkipTypeCheck>
1335 void connect(std::function<ResultT()> callback,
bool quick =
false) {
1361 std::optional<TypedWritePort<ResultT, SkipTypeCheck>>
resultPort;
1366template <
typename ArgT,
bool SkipTypeCheck>
1374 void connect(std::function<
void(
const ArgT &)> callback,
bool quick =
false) {
1386 [
this](
const ArgT &arg) ->
bool {
1399 std::optional<TypedReadPort<ArgT, SkipTypeCheck>>
argPort;
1405template <
bool SkipTypeCheck>
1413 void connect(std::function<
void()> callback,
bool quick =
false) {
1452template <
typename T>
1493 BundlePort *port =
module->resolvePort(AppIDPath{id}, lastLookup);
1496 "' not found in module");
1503template <
typename T>
1506 T *result = port.
getAs<T>();
1509 "' has unexpected type (expected " +
1510 typeid(T).name() +
")");
1517 const std::string &name) {
1518 std::vector<uint32_t> indices;
1519 for (
const auto &[appid, port] : module->
getPorts())
1520 if (appid.name == name && appid.idx.has_value())
1521 indices.push_back(appid.idx.value());
1522 std::sort(indices.begin(), indices.end());
assert(baseType &&"element must be base type")
Arrays have a compile time specified (static) size and an element type.
Bit vectors include signed, unsigned, and signless integers.
Bits are just an array of bits.
Services provide connections to 'bundles' – collections of named, unidirectional communication channe...
T * getAs() const
Cast this Bundle port to a subclass which is actually useful.
Unidirectional channels are the basic communication primitive between the host and accelerator.
const Type * getType() const
const WindowType * getWindowType() const
If this port carries a windowed type, return the original WindowType (whose intoType is what getType(...
Represents either the top level or an instance of a hardware module.
const std::map< AppID, BundlePort & > & getPorts() const
Access the module's ports by ID.
const T & operator[](int idx) const
std::map< int, T > ports_
IndexedPorts(std::map< int, T > &&ports)
IndexedPorts & operator=(const IndexedPorts &)=delete
IndexedPorts(IndexedPorts &&)=default
IndexedPorts & operator=(IndexedPorts &&)=default
bool contains(int idx) const
IndexedPorts(const IndexedPorts &)=delete
A concrete flat message backed by a single vector of bytes.
const uint8_t * getBytes() const
const T * as() const
Cast to a type.
size_t getSize() const
Get the size of the data in bytes.
detail::TypedReadOwnedCallback< T > OutputCallback
QueuedDecodeTypeDeserializer(OutputCallback output)
virtual ~QueuedDecodeTypeDeserializer()=default
std::queue< std::unique_ptr< SerialCoordOutputBatch > > pendingOutputs
bool poke()
Retry delivery of any typed outputs which were previously blocked by the client callback.
bool push(std::unique_ptr< SegmentedMessageData > &msg)
Push one raw message into the deserializer.
virtual DecodedOutputs decode(std::unique_ptr< SegmentedMessageData > &msg)=0
Decode one raw message into zero or more typed outputs.
std::vector< std::unique_ptr< T > > DecodedOutputs
A ChannelPort which reads data from the accelerator.
virtual void connect(ReadCallback callback, const ConnectOptions &options={})
static constexpr uint64_t DefaultMaxDataQueueMsgs
Default max data queue size set at connect time.
Abstract multi-segment message.
virtual MessageData toMessageData() const
Flatten all segments into a standard MessageData.
std::vector< data_frame > pending_frames_
typename Base::DecodedOutputs DecodedOutputs
DecodedOutputs decode(std::unique_ptr< SegmentedMessageData > &msg) override
Decode one raw message into zero or more typed outputs.
typename Base::OutputCallback OutputCallback
std::optional< header_frame > pending_header_
SerialListTypeDeserializer(OutputCallback output)
QueuedDecodeTypeDeserializer< T > Base
typename T::header_frame header_frame
static constexpr size_t kFrameSize
typename T::count_type count_type
typename T::data_frame data_frame
std::vector< uint8_t > partial_
Root class of the ESI type system.
std::string toString(bool oneLine=false) const
services::CallService::Callback * inner
std::optional< TypedReadPort< ArgT, SkipTypeCheck > > argPort
std::optional< TypedWritePort< void > > resultPort
const services::CallService::Callback & raw() const
std::function< void(const ArgT &)> userCallback
void connect(std::function< void(const ArgT &)> callback, bool quick=false)
TypedCallback(const TypedCallback &)=delete
services::CallService::Callback & raw()
TypedCallback & operator=(const TypedCallback &)=delete
TypedCallback(services::CallService::Callback *cb)
std::optional< TypedWritePort< ResultT, SkipTypeCheck > > resultPort
services::CallService::Callback & raw()
std::optional< TypedReadPort< void > > argPort
TypedCallback(const TypedCallback &)=delete
void connect(std::function< ResultT()> callback, bool quick=false)
TypedCallback(services::CallService::Callback *cb)
std::function< ResultT()> userCallback
const services::CallService::Callback & raw() const
services::CallService::Callback * inner
TypedCallback & operator=(const TypedCallback &)=delete
std::optional< TypedWritePort< void > > resultPort
std::optional< TypedReadPort< void > > argPort
TypedCallback(services::CallService::Callback *cb)
services::CallService::Callback & raw()
std::function< void()> userCallback
void connect(std::function< void()> callback, bool quick=false)
TypedCallback & operator=(const TypedCallback &)=delete
TypedCallback(const TypedCallback &)=delete
const services::CallService::Callback & raw() const
services::CallService::Callback * inner
services::CallService::Callback * inner
std::optional< TypedReadPort< ArgT, SkipTypeCheck > > argPort
services::CallService::Callback & raw()
void connect(std::function< ResultT(const ArgT &)> callback, bool quick=false)
TypedCallback(services::CallService::Callback *cb)
std::optional< TypedWritePort< ResultT, SkipTypeCheck > > resultPort
TypedCallback & operator=(const TypedCallback &)=delete
std::function< ResultT(const ArgT &)> userCallback
const services::CallService::Callback & raw() const
TypedCallback(const TypedCallback &)=delete
auto operator()(Args &&...args) -> decltype(this->call(std::forward< Args >(args)...))
Function-call operator overloads: forward to call().
TypedFunction & operator=(const TypedFunction &)=delete
std::optional< TypedWritePort< ArgT, SkipTypeCheck > > argPort
std::future< void > call(First &&first, Rest &&...rest)
Emplace-style call: constructs ArgT in-place from the forwarded arguments and forwards to call(const ...
TypedFunction(const TypedFunction &)=delete
std::optional< TypedReadPort< void > > resultPort
const services::FuncService::Function & raw() const
services::FuncService::Function & raw()
std::future< void > call(const ArgT &arg)
services::FuncService::Function * inner
TypedFunction(services::FuncService::Function *func)
std::optional< TypedReadPort< ResultT, SkipTypeCheck > > resultPort
std::future< ResultT > call()
TypedFunction & operator=(const TypedFunction &)=delete
std::optional< TypedWritePort< void > > argPort
services::FuncService::Function & raw()
TypedFunction(const TypedFunction &)=delete
services::FuncService::Function * inner
std::future< ResultT > operator()()
Function-call operator overload: forwards to call().
const services::FuncService::Function & raw() const
TypedFunction(services::FuncService::Function *func)
const services::FuncService::Function & raw() const
TypedFunction(const TypedFunction &)=delete
TypedFunction & operator=(const TypedFunction &)=delete
services::FuncService::Function * inner
std::future< void > call()
TypedFunction(services::FuncService::Function *func)
std::optional< TypedWritePort< void > > argPort
std::optional< TypedReadPort< void > > resultPort
services::FuncService::Function & raw()
std::future< void > operator()()
Function-call operator overload: forwards to call().
std::optional< TypedWritePort< ArgT, SkipTypeCheck > > argPort
std::optional< TypedReadPort< ResultT, SkipTypeCheck > > resultPort
std::future< ResultT > call(First &&first, Rest &&...rest)
Emplace-style call: constructs ArgT in-place from the forwarded arguments and forwards to call(const ...
TypedFunction(const TypedFunction &)=delete
services::FuncService::Function & raw()
auto operator()(Args &&...args) -> decltype(this->call(std::forward< Args >(args)...))
Function-call operator overloads: forward to call().
std::future< ResultT > call(const ArgT &arg)
TypedFunction & operator=(const TypedFunction &)=delete
const services::FuncService::Function & raw() const
TypedFunction(services::FuncService::Function *func)
Implicit conversion from Function* (returned by getAs<>()).
services::FuncService::Function * inner
TypedReadPort(const TypedReadPort &)=delete
TypedReadPort & operator=(TypedReadPort &&)=delete
TypedReadPort & operator=(const TypedReadPort &)=delete
TypedReadPort(ReadChannelPort &port)
TypedReadPort(ReadChannelPort *port)
TypedReadPort(TypedReadPort &&)=delete
std::future< void > readAsync()
void connect(const ChannelPort::ConnectOptions &opts={std::nullopt, false})
void connect(std::function< bool()> callback, const ChannelPort::ConnectOptions &opts={std::nullopt, false})
const ReadChannelPort & raw() const
TypedReadPort(ReadChannelPort &port)
detail::DeserializerFor< T > Deserializer
TypedReadPort & operator=(const TypedReadPort &)=delete
std::unique_ptr< T > read()
Blocking typed read in polling mode.
void connect(const ChannelPort::ConnectOptions &opts={std::nullopt, false})
Connect in polling mode.
uint64_t maxDataQueueMsgs
void disconnect()
Disconnect the typed port and abandon any pending polling reads.
void setMaxDataQueueMsgs(uint64_t maxMsgs)
Set the maximum number of decoded typed values buffered in polling mode.
void emplaceDeserializer(detail::DeserializerOutputCallback< T > callback)
void connect(std::function< bool(const T &)> callback, const ChannelPort::ConnectOptions &opts={std::nullopt, false})
Connect a non-owning typed callback.
const ReadChannelPort & raw() const
TypedReadPort & operator=(TypedReadPort &&)=delete
std::optional< PollingState > pollingState
TypedReadPort(const TypedReadPort &)=delete
std::optional< Deserializer > deserializer
void connect(detail::TypedReadOwnedCallback< T > callback, const ChannelPort::ConnectOptions &opts={std::nullopt, false})
Connect an owning typed callback.
detail::PollingBuffer< std::unique_ptr< T > > PollingState
std::future< std::unique_ptr< T > > readAsync()
Asynchronous typed read in polling mode.
TypedReadPort(ReadChannelPort *port)
TypedReadPort(TypedReadPort &&)=delete
TypedWritePort(WriteChannelPort &port)
TypedWritePort(WriteChannelPort *port)
void connect(const ChannelPort::ConnectOptions &opts={std::nullopt, false})
const WriteChannelPort & raw() const
const WriteChannelPort & raw() const
void connect(const ChannelPort::ConnectOptions &opts={std::nullopt, false})
void write(std::unique_ptr< T > &data)
Write by taking ownership.
bool tryWrite(const T &data)
bool tryWrite(std::unique_ptr< T > &data)
Non-blocking write by taking ownership.
TypedWritePort(WriteChannelPort &port)
void write(const T &data)
TypedWritePort(WriteChannelPort *port)
The "void" type is a special type which can be used to represent no type.
Windows represent a fixed-size sliding window over a stream of data.
A ChannelPort which sends data to the accelerator.
virtual void connect(const ConnectOptions &options={}) override
Set up a connection to the accelerator.
Default deserializer for simple 1:1 typed reads.
bool push(std::unique_ptr< SegmentedMessageData > &msg)
TypedReadOwnedCallback< T > OutputCallback
std::unique_ptr< T > pendingOutput
PODTypeDeserializer(OutputCallback output, WireInfo wireInfo)
Shared queue/promise helper for polling-style read APIs.
A function call which gets attached to a service port.
A function call which gets attached to a service port.
constexpr bool has_type_deserializer_v
std::function< bool(std::unique_ptr< T > &)> TypedReadOwnedCallback
Owning callback used by typed read deserializers.
ChannelPort::ConnectOptions typedFunctionConnectOptions()
Standard ConnectOptions for typed function ports: untranslated frames so the deserializer can see raw...
void throwAlreadyConnected()
Throw a clear "already connected" error from connect() paths.
typename DeserializerSelector< T >::type DeserializerFor
void throwNullFunction()
Throw the standard "null Function pointer" error used by every TypedFunction specialization.
void throwNotConnected()
Throw a clear "not connected" error from call() paths.
void throwCallbackAlreadyConnected()
Throw a clear "already connected" error from TypedCallback connect() paths.
void throwNullCallback()
Throw the standard "null Callback pointer" error used by every TypedCallback specialization.
typename DeserializerFor< T >::OutputCallback DeserializerOutputCallback
std::future< T > awaitDecoded(std::future< std::unique_ptr< T > > inner)
Convert a std::future<std::unique_ptr<T>> (as returned by TypedReadPort::readAsync()) into a std::fut...
const MessageData & getMessageDataRef(const SegmentedMessageData &msg, MessageData &scratch)
std::string toString(const std::any &a)
'Stringify' a std::any. This is used to log std::any values by some loggers.
void verifyTypeCompatibility(const Type *portType)
MessageData toMessageData(const T &data, WireInfo wi)
Pack a C++ integral value into a MessageData with the given wire byte count.
std::vector< uint32_t > findPortIndices(HWModule *module, const std::string &name)
Return a sorted vector of the idx values for every port whose AppID name matches name.
WireInfo getWireInfo(const Type *portType)
BundlePort & findPortOrThrow(HWModule *module, const AppID &id)
Look up a BundlePort by AppID in module.
constexpr bool has_esi_id_v
T * findPortAsOrThrow(HWModule *module, const AppID &id)
Look up a BundlePort by AppID and cast it to T.
constexpr bool has_esi_window_id_v
constexpr bool is_std_array_v
T fromMessageData(const MessageData &msg, WireInfo wi)
Unpack a MessageData into a C++ integral value with the given wire info.
const Type * unwrapTypeAlias(const Type *type)
Unwrap TypeAliasType (possibly recursively) to get the underlying type.
Compute the wire byte count for a port type.
typename T::TypeDeserializer type
PODTypeDeserializer< T > type