14#include <boost/algorithm/string.hpp>
15#include <boost/algorithm/string/predicate.hpp>
19using namespace std::string_literals;
29 std::string address, std::map<std::string, std::string> parameters) {
30 if(parameters[
"map"].empty()) {
34 if(!address.empty()) {
35 if(parameters.size() > 1) {
38 "other than the map file in the device descriptor.");
41 std::vector<std::string> tokens;
42 boost::split(tokens, address, boost::is_any_of(
","));
43 if(tokens.size() != 3) {
45 "parameters in the address string.");
47 parameters[
"type"] = tokens[0];
48 parameters[
"device"] = tokens[1];
49 parameters[
"area"] = tokens[2];
61 if(parameters[
"type"].empty()) {
66 if(parameters[
"device"].empty()) {
68 "specified in the device descriptor.");
73 if(parameters[
"type"] ==
"area") {
76 else if(parameters[
"type"] ==
"areaHandshake") {
80 else if(parameters[
"type"] ==
"3regs") {
83 else if(parameters[
"type"] ==
"2regs") {
86 if(parameters[
"sleep"].empty()) {
88 "descriptor for type '2regs'.");
91 else if(parameters[
"type"] ==
"regWindow") {
101 if(parameters[
"area"].empty()) {
103 "must be specified in the device "
104 "descriptor for types 'area' and 'areaHandshake'.");
110 if((parameters[
"type"] ==
"3regs") || (parameters[
"type"] ==
"2regs")) {
111 if(parameters[
"data"].empty()) {
113 "name must be specified in the device "
114 "descriptor for types '2regs' and '3regs'.");
120 if(parameters[
"address"].empty()) {
122 "name must be specified in the device "
123 "descriptor for type '2regs', '3regs' and 'regWindow'.");
127 if(!parameters[
"dataDelay"].empty()) {
131 catch(std::exception& e) {
133 parameters[
"dataDelay"] +
"': " + e.what());
138 if(parameters[
"readData"].empty() && parameters[
"writeData"].empty() && parameters[
"data"].empty()) {
140 "SubdeviceBackend: Either readData or writeData must be specified in RegisterWindow mode.");
142 if((parameters[
"readData"].empty() && !parameters[
"readRequest"].empty()) ||
143 (!parameters[
"readData"].empty() && parameters[
"readRequest"].empty())) {
145 "RegisterWindow mode (or not at all).");
149 if(!parameters[
"chipIndex"].empty()) {
151 _chipIndex = std::stoul(parameters[
"chipIndex"]);
153 catch(std::exception& e) {
155 parameters[
"chipIndex"] +
"': " + e.what());
162 "name must be specified in the device "
163 "descriptor for types '3regs' and 'areaHandshake'.");
171 if(!parameters[
"timeout"].empty()) {
173 _timeout = std::stoul(parameters[
"timeout"]);
175 catch(std::exception& e) {
177 "SubdeviceBackend: Invalid value for parameter 'timeout': '" + parameters[
"timeout"] +
"': " + e.what());
182 if(!parameters[
"sleep"].empty()) {
186 catch(std::exception& e) {
188 "SubdeviceBackend: Invalid value for parameter 'sleep': '" + parameters[
"sleep"] +
"': " + e.what());
192 if(parameters[
"map"].empty()) {
206 if(parameters[
"readData"].empty()) {
213 else if(parameters[
"writeData"].empty() && parameters[
"data"].empty()) {
238 mtx = std::make_shared<std::mutex>();
276 template<
typename UserType>
280 boost::shared_ptr<NDRegisterAccessor<UserType>> returnValue;
282 returnValue = getAreaRegisterAccessor<UserType>(registerPathName, numberOfWords, wordOffsetInRegister, flags);
286 getSynchronisedRegisterAccessor<UserType>(registerPathName, numberOfWords, wordOffsetInRegister, flags);
289 returnValue = getRegisterWindowAccessor<UserType>(registerPathName, numberOfWords, wordOffsetInRegister, flags);
294 returnValue->setExceptionBackend(shared_from_this());
302 size_t wordOffsetInRegister,
bool forceAlignment) {
307 std::cout <<
"SubdeviceBackend: WARNING: BAR others then 0 detected. BAR 0 will be used instead. Register "
320 if(wordOffsetInRegister != 0) {
322 "threeRegisters and twoRegisters. Register " +
325 if((numberOfWords != 0) && (numberOfWords != info.
nElements)) {
327 "threeRegisters and twoRegisters. Register " +
334 size_t byteOffset = info.
address +
sizeof(int32_t) * wordOffsetInRegister;
335 if(forceAlignment && (byteOffset % 4 != 0)) {
337 "multiple of 4 are supported.");
341 if(numberOfWords == 0) {
344 else if(numberOfWords > info.
nElements) {
346 " elements from register '" + info.
pathName +
"', which only has a length of " +
352 if(numberOfWords + wordOffsetInRegister > info.
nElements) {
354 "SubdeviceBackend: Requested offset + number of words exceeds the size of the register '" + info.
pathName +
361 template<
typename UserType>
374 size_t wordOffset = (info.address +
sizeof(int32_t) * wordOffsetInRegister) / 4;
381 return boost::make_shared<ConvertingDecorator<UserType, int32_t>>(rawAcc, info);
384 throw ChimeraTK::logic_error(
"Given UserType when obtaining the SubdeviceBackend in raw mode does not "s +
385 "match the expected type. Use an int32_t instead! (Register name: " + registerPathName +
"')");
400 "2reg or areaHandshake must have writeable registers only!");
404 boost::shared_ptr<NDRegisterAccessor<int32_t>> accAddress, accData;
415 size_t wordOffset = (info.
address +
sizeof(int32_t) * wordOffsetInRegister) / 4;
419 boost::shared_ptr<NDRegisterAccessor<int32_t>> accStatus;
424 size_t byteOffset = info.
address +
sizeof(int32_t) * wordOffsetInRegister;
425 auto sharedThis = boost::enable_shared_from_this<DeviceBackend>::shared_from_this();
427 return boost::make_shared<SubdeviceRegisterAccessor>(boost::dynamic_pointer_cast<SubdeviceBackend>(sharedThis),
428 info.
pathName, accAddress, accData, accStatus, byteOffset, numberOfWords);
433 template<
typename UserType>
435 const RegisterPath& registerPathName,
size_t numberOfWords,
size_t wordOffsetInRegister,
443 return boost::make_shared<ConvertingDecorator<UserType, int32_t>>(rawAcc, info);
446 if constexpr(std::is_same_v<UserType, int32_t>) {
449 return boost::make_shared<ConvertingRawDecorator<UserType>>(rawAcc, info);
451 throw ChimeraTK::logic_error(
"Given UserType when obtaining the SubdeviceBackend in raw mode does not "s +
452 "match the expected type. Use an int32_t instead! (Register name: '" + registerPathName +
"')");
457 template<
typename UserType>
459 const RegisterPath& registerPathName,
size_t numberOfWords,
size_t wordOffsetInRegister,
467 boost::shared_ptr<NDRegisterAccessor<UserType>> retVal;
468 callForRawType(info.getDataDescriptor().rawDataType(), [&](
auto arg) {
469 using uRawType = std::make_unsigned_t<decltype(arg)>;
472 DataType nativeReadWriteType;
473 auto targetCatalogue = _targetDevice->getRegisterCatalogue();
475 auto writeDataRegName = _parameters[
"writeData"];
476 if(writeDataRegName.empty()) {
478 writeDataRegName = _parameters[
"data"];
481 if(!writeDataRegName.empty()) {
482 auto writeAccInfo = targetCatalogue.getRegister(writeDataRegName);
483 nativeReadWriteType = writeAccInfo.getDataDescriptor().minimumDataType();
486 auto readAccInfo = targetCatalogue.getRegister(_parameters[
"readData"]);
487 if(nativeReadWriteType != DataType::none) {
488 if(readAccInfo.getDataDescriptor().minimumDataType() != nativeReadWriteType) {
489 throw ChimeraTK::logic_error(
"Bad map/firmware file: " + writeDataRegName +
490 " must have the same data type as " + _parameters[
"readData"]);
494 nativeReadWriteType = readAccInfo.getDataDescriptor().minimumDataType();
498 auto sharedThis = boost::enable_shared_from_this<DeviceBackend>::shared_from_this();
500 boost::shared_ptr<NDRegisterAccessor<uRawType>> rawAcc;
501 callForType(nativeReadWriteType, [&](
auto rwTypeArg) {
502 if constexpr(std::is_integral_v<
decltype(rwTypeArg)>) {
505 boost::dynamic_pointer_cast<SubdeviceBackend>(sharedThis), registerPathName, numberOfWords,
506 wordOffsetInRegister);
512 retVal = boost::make_shared<ConvertingDecorator<UserType, uRawType>>(rawAcc, info);
516 if constexpr(std::is_same_v<UserType, uRawType>) {
519 retVal = boost::make_shared<ConvertingRawDecorator<UserType>>(rawAcc, info);
521 else if constexpr(std::is_same_v<UserType, std::make_signed_t<uRawType>>) {
526 boost::shared_ptr<NDRegisterAccessor<UserType>> signedRawAcc;
527 callForType(nativeReadWriteType, [&](
auto rwTypeArg) {
528 if constexpr(std::is_integral_v<
decltype(rwTypeArg)>) {
529 signedRawAcc = boost::make_shared<
530 SubdeviceRegisterWindowAccessor<UserType, std::make_unsigned_t<
decltype(rwTypeArg)>>>(
531 boost::dynamic_pointer_cast<SubdeviceBackend>(sharedThis), registerPathName, numberOfWords,
532 wordOffsetInRegister);
536 retVal = boost::make_shared<ConvertingRawDecorator<UserType>>(signedRawAcc, info);
539 throw ChimeraTK::logic_error(
"Given UserType when obtaining the SubdeviceBackend in raw mode does not "s +
540 "match the expected type. Use an unsigned int matching the bit width in the map file! (Register name: '" +
541 registerPathName +
"')");
551 boost::shared_ptr<NDRegisterAccessor<int32_t>> SubdeviceBackend::getAreaRegisterAccessor<int32_t>(
552 const RegisterPath& registerPathName,
size_t numberOfWords,
size_t wordOffsetInRegister, AccessModeFlags flags) {
553 assert(_type == Type::area);
556 auto info = _registerMap.getBackendRegister(registerPathName);
557 verifyRegisterAccessorSize(info, numberOfWords, wordOffsetInRegister,
true);
560 bool isRaw = flags.has(AccessMode::raw);
563 size_t wordOffset = (info.address +
sizeof(int32_t) * wordOffsetInRegister) / 4;
564 flags.add(AccessMode::raw);
565 auto rawAcc = _targetDevice->getRegisterAccessor<int32_t>(_targetArea, numberOfWords, wordOffset, flags);
570 return boost::make_shared<ConvertingDecorator<int32_t, int32_t>>(rawAcc, info);
572 return boost::make_shared<ConvertingRawDecorator<int32_t>>(rawAcc, info);
577 void SubdeviceBackend::setExceptionImpl() noexcept {
578 obtainTargetBackend();
579 _targetDevice->setException(getActiveExceptionMessage());
584 void SubdeviceBackend::activateAsyncRead() noexcept {
585 obtainTargetBackend();
586 _targetDevice->activateAsyncRead();
590 std::set<DeviceBackend::BackendID> SubdeviceBackend::getInvolvedBackendIDs() {
591 obtainTargetBackend();
592 std::set<DeviceBackend::BackendID> retVal{getBackendID()};
593 retVal.merge(_targetDevice->getInvolvedBackendIDs());
#define FILL_VIRTUAL_FUNCTION_TEMPLATE_VTABLE(functionName)
Fill the vtable of a virtual function template defined with DEFINE_VIRTUAL_FUNCTION_TEMPLATE.
Set of AccessMode flags with additional functionality for an easier handling.
bool has(AccessMode flag) const
Check if a certain flag is in the set.
void add(AccessMode flag)
Add the given flag to the set.
void checkForUnknownFlags(const std::set< AccessMode > &knownFlags) const
Check of any flag which is not in the given set "knownFlags" is set.
BackendFactory is a the factory class to create devices.
static BackendFactory & getInstance()
Static function to get an instance of factory.
boost::shared_ptr< DeviceBackend > createBackend(const std::string &aliasOrUri)
Create a new backend and return the instance as a shared pointer.
void modifyRegister(const BackendRegisterInfo ®isterInfo)
Replaces the register information for the matching register.
void setOpenedAndClearException() noexcept
Backends should call this function at the end of a (successful) open() call.
std::atomic< bool > _opened
flag if backend is opened
static std::pair< NumericAddressedRegisterCatalogue, MetadataCatalogue > parse(const std::string &fileName)
Performs parsing of specified MAP file, resulting in catalogue objects describing all registers and m...
NumericAddressedRegisterInfo getBackendRegister(const RegisterPath ®isterPathName) const override
Note: Override this function if backend has "hidden" registers which are not added to the map and hen...
std::unique_ptr< BackendRegisterCatalogueBase > clone() const override
Create deep copy of the catalogue.
uint32_t nElements
Number of elements in register.
std::vector< ChannelInfo > channels
Define per-channel information (bit interpretation etc.), 1D/scalars have exactly one entry.
uint64_t bar
Upper part of the address (name originally from PCIe, meaning now generalised)
uint64_t address
Lower part of the address relative to BAR, in bytes.
bool isWriteable() const override
Return whether the register is writeable.
Catalogue of register information.
Class to store a register path name.
Backend for subdevices which are passed through some register or area of another device (subsequently...
boost::shared_ptr< NDRegisterAccessor< UserType > > getAreaRegisterAccessor(const RegisterPath ®isterPathName, size_t numberOfWords, size_t wordOffsetInRegister, AccessModeFlags flags)
getRegisterAccessor implementation for area types
std::shared_ptr< std::mutex > _mutex
NumericAddressedRegisterCatalogue _registerMap
map from register names to addresses
void open() override
Open the device.
Type _type
type of the subdevice
boost::shared_ptr< NDRegisterAccessor< UserType > > getRegisterWindowAccessor(const RegisterPath ®isterPathName, size_t numberOfWords, size_t wordOffsetInRegister, const AccessModeFlags &flags)
static boost::shared_ptr< DeviceBackend > createInstance(std::string address, std::map< std::string, std::string > parameters)
void close() override
Close the device.
std::string _targetControl
MetadataCatalogue getMetadataCatalogue() const override
Return the device metadata catalogue.
std::pair< DeviceBackend *, RegisterPath > BusyRegisterKey
size_t _addressToDataDelay
for type == registerWindow, threeRegisters or twoRegisters: sleep time between address and data write
boost::shared_ptr< ChimeraTK::DeviceBackend > _targetDevice
The target device backend itself.
size_t _timeout
timeout (in milliseconds), used in threeRegisters to throw a runtime_error if status register stuck a...
size_t _chipIndex
for type == registerWindow: chip index
boost::shared_ptr< NDRegisterAccessor< UserType > > getSynchronisedRegisterAccessor(const RegisterPath ®isterPathName, size_t numberOfWords, size_t wordOffsetInRegister, const AccessModeFlags &flags)
getRegisterAccessor implementation for threeRegisters types
MetadataCatalogue _metadataCatalogue
SubdeviceBackend(std::map< std::string, std::string > parameters)
void obtainTargetBackend()
obtain the target backend if not yet done
std::map< std::string, std::string > _parameters
currently only used for type == registerWindow: the whole set of parameters
size_t _sleepTime
for type == registerWindow, threeRegisters or twoRegisters: sleep time of polling loop resp.
boost::shared_ptr< NDRegisterAccessor< UserType > > getRegisterAccessor_impl(const RegisterPath ®isterPathName, size_t numberOfWords, size_t wordOffsetInRegister, AccessModeFlags flags)
std::string _targetAlias
the target device name
std::string _targetAddress
for type == registerWindow, threeRegisters or twoRegisters: the names of the basic target registers
std::string _targetArea
for type == area: the name of the target register
void verifyRegisterAccessorSize(const NumericAddressedRegisterInfo &info, size_t &numberOfWords, size_t wordOffsetInRegister, bool enforceAlignment)
Check consistency of the passed sizes and offsets against the information in the map file Will adjust...
std::string _targetWriteData
static std::mutex _mutexMapMutex
RegisterCatalogue getRegisterCatalogue() const override
Return the register catalogue with detailed information on all registers.
static std::map< BusyRegisterKey, std::shared_ptr< std::mutex > > _mutexes
Mutex to protext concurrent access to the target registers.
boost::shared_ptr< SubdeviceRegisterAccessor > accessorCreationHelper(const NumericAddressedRegisterInfo &info, size_t numberOfWords, size_t wordOffsetInRegister, AccessModeFlags flags)
The RegisterRawType is determined by the number of bytes in the SubDevice mapfile The ReadWriteDataTy...
Exception thrown when a logic error has occured.
void callForRawType(const DataType &type, LAMBDATYPE lambda)
callForRawType() is similar to callForType(), just with a subset of supported data types which can be...
void callForType(const std::type_info &type, LAMBDATYPE lambda)
Helper function for running code which uses some compile-time type that is specified at runtime as a ...
@ raw
Raw access: disable any possible conversion from the original hardware data type into the given UserT...
std::string to_string(const std::string &v)