ChimeraTK-DeviceAccess 03.27.00
Loading...
Searching...
No Matches
SubdeviceBackend.cc
Go to the documentation of this file.
1// SPDX-FileCopyrightText: Deutsches Elektronen-Synchrotron DESY, MSK, ChimeraTK Project <chimeratk-support@desy.de>
2// SPDX-License-Identifier: LGPL-3.0-or-later
3
4#include "SubdeviceBackend.h"
5
6#include "BackendFactory.h"
7#include "Exception.h"
8#include "MapFileParser.h"
13
14#include <boost/algorithm/string.hpp>
15#include <boost/algorithm/string/predicate.hpp>
16
17#include <utility>
18
19using namespace std::string_literals;
20
21namespace ChimeraTK {
22
23 std::map<SubdeviceBackend::BusyRegisterKey, std::shared_ptr<std::mutex>> SubdeviceBackend::_mutexes;
25
26 /********************************************************************************************************************/
27
28 boost::shared_ptr<DeviceBackend> SubdeviceBackend::createInstance(
29 std::string address, std::map<std::string, std::string> parameters) {
30 if(parameters["map"].empty()) {
31 throw ChimeraTK::logic_error("Map file name not specified.");
32 }
33
34 if(!address.empty()) {
35 if(parameters.size() > 1) {
36 throw ChimeraTK::logic_error("SubdeviceBackend: You cannot specify both the address string and "
37 "parameters "
38 "other than the map file in the device descriptor.");
39 }
40 // decode target information from the instance
41 std::vector<std::string> tokens;
42 boost::split(tokens, address, boost::is_any_of(","));
43 if(tokens.size() != 3) {
44 throw ChimeraTK::logic_error("SubdeviceBackend: There must be exactly 3 "
45 "parameters in the address string.");
46 }
47 parameters["type"] = tokens[0];
48 parameters["device"] = tokens[1];
49 parameters["area"] = tokens[2];
50 }
51
52 return boost::shared_ptr<DeviceBackend>(new SubdeviceBackend(parameters));
53 }
54
55 /********************************************************************************************************************/
56
57 SubdeviceBackend::SubdeviceBackend(std::map<std::string, std::string> parameters) : _parameters(parameters) {
59
60 // check if type is specified
61 if(parameters["type"].empty()) {
62 throw ChimeraTK::logic_error("SubdeviceBackend: Type must be specified in the device descriptor.");
63 }
64
65 // check if target device is specified and open the target device
66 if(parameters["device"].empty()) {
67 throw ChimeraTK::logic_error("SubdeviceBackend: Target device name must be "
68 "specified in the device descriptor.");
69 }
70 _targetAlias = parameters["device"];
71
72 // type "area":
73 if(parameters["type"] == "area") {
75 }
76 else if(parameters["type"] == "areaHandshake") {
78 }
79 // type "3regs":
80 else if(parameters["type"] == "3regs") {
82 }
83 else if(parameters["type"] == "2regs") {
85
86 if(parameters["sleep"].empty()) {
87 throw ChimeraTK::logic_error("SubdeviceBackend: Target sleep time must be specified in the device "
88 "descriptor for type '2regs'.");
89 }
90 }
91 else if(parameters["type"] == "regWindow") {
93 }
94 // unknown type
95 else {
96 throw ChimeraTK::logic_error("SubdeviceBackend: Unknown type '" + parameters["type"] + "' specified.");
97 }
98
99 if(needAreaParam()) {
100 // check if target register name is specified
101 if(parameters["area"].empty()) {
102 throw ChimeraTK::logic_error("SubdeviceBackend: Target register name "
103 "must be specified in the device "
104 "descriptor for types 'area' and 'areaHandshake'.");
105 }
106 _targetArea = parameters["area"];
107 }
108 else {
109 // if area is not given, data is required for 2reg and 3reg
110 if((parameters["type"] == "3regs") || (parameters["type"] == "2regs")) {
111 if(parameters["data"].empty()) {
112 throw ChimeraTK::logic_error("SubdeviceBackend: Target data register "
113 "name must be specified in the device "
114 "descriptor for types '2regs' and '3regs'.");
115 }
116 _targetWriteData = parameters["data"];
117 }
118
119 // check if all target register names are specified
120 if(parameters["address"].empty()) {
121 throw ChimeraTK::logic_error("SubdeviceBackend: Target address register "
122 "name must be specified in the device "
123 "descriptor for type '2regs', '3regs' and 'regWindow'.");
124 }
125 _targetAddress = parameters["address"];
126 // optional parameter for delay between address write and data write
127 if(!parameters["dataDelay"].empty()) {
128 try {
129 _addressToDataDelay = std::stoul(parameters["dataDelay"]);
130 }
131 catch(std::exception& e) {
132 throw ChimeraTK::logic_error("SubdeviceBackend: Invalid value for parameter 'dataDelay': '" +
133 parameters["dataDelay"] + "': " + e.what());
134 }
135 }
137 // we can have three more entries
138 if(parameters["readData"].empty() && parameters["writeData"].empty() && parameters["data"].empty()) {
140 "SubdeviceBackend: Either readData or writeData must be specified in RegisterWindow mode.");
141 }
142 if((parameters["readData"].empty() && !parameters["readRequest"].empty()) ||
143 (!parameters["readData"].empty() && parameters["readRequest"].empty())) {
144 throw ChimeraTK::logic_error("SubdeviceBackend: readData and readRequest must both be specified in "
145 "RegisterWindow mode (or not at all).");
146 }
147
148 // The chip index is optional. It defaults to 0.
149 if(!parameters["chipIndex"].empty()) {
150 try {
151 _chipIndex = std::stoul(parameters["chipIndex"]);
152 }
153 catch(std::exception& e) {
154 throw ChimeraTK::logic_error("SubdeviceBackend: Invalid value for parameter 'chipIndex': '" +
155 parameters["chipIndex"] + "': " + e.what());
156 }
157 }
158 }
159 }
160 if(needStatusParam() && parameters["status"].empty()) {
161 throw ChimeraTK::logic_error("SubdeviceBackend: Target status register "
162 "name must be specified in the device "
163 "descriptor for types '3regs' and 'areaHandshake'.");
164 }
165 _targetControl = parameters["status"];
166 if(_targetControl.empty()) {
167 // try the regWindow syntax
168 _targetControl = parameters["busy"];
169 }
170 if(!_targetControl.empty()) {
171 if(!parameters["timeout"].empty()) {
172 try {
173 _timeout = std::stoul(parameters["timeout"]);
174 }
175 catch(std::exception& e) {
177 "SubdeviceBackend: Invalid value for parameter 'timeout': '" + parameters["timeout"] + "': " + e.what());
178 }
179 }
180 }
181 // sleep parameter for 2regs, 3regs or areaHandshake case
182 if(!parameters["sleep"].empty()) {
183 try {
184 _sleepTime = std::stoul(parameters["sleep"]);
185 }
186 catch(std::exception& e) {
188 "SubdeviceBackend: Invalid value for parameter 'sleep': '" + parameters["sleep"] + "': " + e.what());
189 }
190 }
191 // parse map file
192 if(parameters["map"].empty()) {
193 throw ChimeraTK::logic_error("SubdeviceBackend: Map file must be specified.");
194 }
195 std::tie(_registerMap, _metadataCatalogue) = MapFileParser::parse(parameters["map"]);
197 // Turn off readable flag in 2reg/3reg mode
198 for(auto info : _registerMap) {
199 // we are modifying a copy here
201 _registerMap.modifyRegister(info); // Should be OK. Should not change the iterator
202 }
203 }
205 // Turn off readable if registerWindow is write only
206 if(parameters["readData"].empty()) {
207 for(auto info : _registerMap) {
208 // we are modifying a copy here
210 _registerMap.modifyRegister(info); // Should be OK. Should not change the iterator
211 }
212 }
213 else if(parameters["writeData"].empty() && parameters["data"].empty()) {
214 for(auto info : _registerMap) {
215 // we are modifying a copy here
217 _registerMap.modifyRegister(info); // Should be OK. Should not change the iterator
218 }
219 }
220 }
221 }
222
223 /********************************************************************************************************************/
224
226 if(_targetDevice != nullptr) {
227 assert(_mutex);
228 return;
229 }
230 BackendFactory& factoryInstance = BackendFactory::getInstance();
231 _targetDevice = factoryInstance.createBackend(_targetAlias);
232
233 // Now that the device pointer is known, we can get the mutex from the shared map
234 BusyRegisterKey brKey = std::make_pair(_targetDevice.get(), RegisterPath(_targetControl));
235 std::lock_guard l(_mutexMapMutex);
236 auto& mtx = _mutexes[brKey];
237 if(!mtx) {
238 mtx = std::make_shared<std::mutex>();
239 }
240
241 assert(!_mutex);
242 _mutex = mtx;
243 }
244
245 /********************************************************************************************************************/
246
249 // open target backend, unconditionally as it is also used for recovery
250 _targetDevice->open();
252 }
253
254 /********************************************************************************************************************/
255
258 _targetDevice->close();
259 _opened = false;
260 }
261
262 /********************************************************************************************************************/
263
267
268 /********************************************************************************************************************/
269
273
274 /********************************************************************************************************************/
275
276 template<typename UserType>
277 boost::shared_ptr<NDRegisterAccessor<UserType>> SubdeviceBackend::getRegisterAccessor_impl(
278 const RegisterPath& registerPathName, size_t numberOfWords, size_t wordOffsetInRegister, AccessModeFlags flags) {
280 boost::shared_ptr<NDRegisterAccessor<UserType>> returnValue;
281 if(_type == Type::area) {
282 returnValue = getAreaRegisterAccessor<UserType>(registerPathName, numberOfWords, wordOffsetInRegister, flags);
283 }
285 returnValue =
286 getSynchronisedRegisterAccessor<UserType>(registerPathName, numberOfWords, wordOffsetInRegister, flags);
287 }
288 else if(_type == Type::registerWindow) {
289 returnValue = getRegisterWindowAccessor<UserType>(registerPathName, numberOfWords, wordOffsetInRegister, flags);
290 }
291 if(!returnValue) {
292 throw ChimeraTK::logic_error("Unknown type");
293 }
294 returnValue->setExceptionBackend(shared_from_this());
295
296 return returnValue;
297 }
298
299 /********************************************************************************************************************/
300
302 size_t wordOffsetInRegister, bool forceAlignment) {
303 // check that the bar is 0
304 if(info.bar != 0) {
305 // throw ChimeraTK::logic_error("SubdeviceBackend: BARs other then 0 are not supported. Register '" +
306 // registerPathName + "' is in BAR " + std::to_string(info->bar) + ".");
307 std::cout << "SubdeviceBackend: WARNING: BAR others then 0 detected. BAR 0 will be used instead. Register "
308 << info.pathName << " is in BAR " << std::to_string(info.bar) << "." << std::endl;
309 }
310
311 // check that the register is not a 2D multiplexed register, which is not yet
312 // supported
313 if(info.channels.size() != 1) {
314 throw ChimeraTK::logic_error("SubdeviceBackend: 2D multiplexed registers are not yet supported.");
315 }
316
317 // Partial accessors are not implemented correctly yet. Better throw than getting something that seems to work but
318 // does the wrong thing.
320 if(wordOffsetInRegister != 0) {
321 throw ChimeraTK::logic_error("SubdeviceBackend: Partial accessors are not supported yet for type "
322 "threeRegisters and twoRegisters. Register " +
323 info.pathName + " has requested offset " + std::to_string(wordOffsetInRegister));
324 }
325 if((numberOfWords != 0) && (numberOfWords != info.nElements)) {
326 throw ChimeraTK::logic_error("SubdeviceBackend: Partial accessors are not supported yet for type "
327 "threeRegisters and twoRegisters. Register " +
328 info.pathName + " has requested nElements " + std::to_string(numberOfWords) + " of " +
330 }
331 }
332
333 // compute full offset (from map file and function arguments)
334 size_t byteOffset = info.address + sizeof(int32_t) * wordOffsetInRegister;
335 if(forceAlignment && (byteOffset % 4 != 0)) {
336 throw ChimeraTK::logic_error("SubdeviceBackend: Only addresses which are a "
337 "multiple of 4 are supported.");
338 }
339
340 // compute effective length
341 if(numberOfWords == 0) {
342 numberOfWords = info.nElements;
343 }
344 else if(numberOfWords > info.nElements) {
345 throw ChimeraTK::logic_error("SubdeviceBackend: Requested " + std::to_string(numberOfWords) +
346 " elements from register '" + info.pathName + "', which only has a length of " +
347 std::to_string(info.nElements) + " elements.");
348 }
349
350 // Check that the requested register fits in the register description. The downstream register might be larger
351 // so we cannot delegate the check
352 if(numberOfWords + wordOffsetInRegister > info.nElements) {
354 "SubdeviceBackend: Requested offset + number of words exceeds the size of the register '" + info.pathName +
355 "'!");
356 }
357 }
358
359 /********************************************************************************************************************/
360
361 template<typename UserType>
362 boost::shared_ptr<NDRegisterAccessor<UserType>> SubdeviceBackend::getAreaRegisterAccessor(
363 const RegisterPath& registerPathName, size_t numberOfWords, size_t wordOffsetInRegister, AccessModeFlags flags) {
364 assert(_type == Type::area);
365
366 // obtain register info
367 auto info = _registerMap.getBackendRegister(registerPathName);
368 verifyRegisterAccessorSize(info, numberOfWords, wordOffsetInRegister, true);
369
370 // store raw flag for later (since we modify the flags)
371 bool isRaw = flags.has(AccessMode::raw);
372
373 // obtain target accessor in raw mode
374 size_t wordOffset = (info.address + sizeof(int32_t) * wordOffsetInRegister) / 4;
375 flags.add(AccessMode::raw);
376 auto rawAcc = _targetDevice->getRegisterAccessor<int32_t>(_targetArea, numberOfWords, wordOffset, flags);
377
378 // decorate with appropriate FixedPointConvertingDecorator. This is done even
379 // when in raw mode so we can properly implement getAsCooked()/setAsCooked().
380 if(!isRaw) {
381 return boost::make_shared<ConvertingDecorator<UserType, int32_t>>(rawAcc, info);
382 }
383 // this is handled by the template specialisation for int32_t
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 + "')");
386 }
387
388 /********************************************************************************************************************/
389
390 boost::shared_ptr<SubdeviceRegisterAccessor> SubdeviceBackend::accessorCreationHelper(
391 const NumericAddressedRegisterInfo& info, size_t numberOfWords, size_t wordOffsetInRegister,
392 AccessModeFlags flags) {
394
395 verifyRegisterAccessorSize(info, numberOfWords, wordOffsetInRegister, false);
396
397 // check if register access properly specified in map file
398 if(!info.isWriteable()) {
399 throw ChimeraTK::logic_error("SubdeviceBackend: Subdevices of type 3reg or "
400 "2reg or areaHandshake must have writeable registers only!");
401 }
402
403 // obtain target accessors
404 boost::shared_ptr<NDRegisterAccessor<int32_t>> accAddress, accData;
405 if(!needAreaParam()) {
406 accAddress = _targetDevice->getRegisterAccessor<int32_t>(_targetAddress, 1, 0, {});
407 accData = _targetDevice->getRegisterAccessor<int32_t>(_targetWriteData, 0, 0, {});
408 }
409 else {
410 // check alignment just like it is done in 'area' type subdevice which is based on raw int32 accessors to target
411 verifyRegisterAccessorSize(info, numberOfWords, wordOffsetInRegister, true);
412
413 // obtain target accessor in raw mode
414 // FIXME: Should not be a raw accessor here
415 size_t wordOffset = (info.address + sizeof(int32_t) * wordOffsetInRegister) / 4;
416 flags.add(AccessMode::raw);
417 accData = _targetDevice->getRegisterAccessor<int32_t>(_targetArea, numberOfWords, wordOffset, flags);
418 }
419 boost::shared_ptr<NDRegisterAccessor<int32_t>> accStatus;
420 if(!_targetControl.empty()) {
421 accStatus = _targetDevice->getRegisterAccessor<int32_t>(_targetControl, 1, 0, {});
422 }
423
424 size_t byteOffset = info.address + sizeof(int32_t) * wordOffsetInRegister;
425 auto sharedThis = boost::enable_shared_from_this<DeviceBackend>::shared_from_this();
426
427 return boost::make_shared<SubdeviceRegisterAccessor>(boost::dynamic_pointer_cast<SubdeviceBackend>(sharedThis),
428 info.pathName, accAddress, accData, accStatus, byteOffset, numberOfWords);
429 }
430
431 /********************************************************************************************************************/
432
433 template<typename UserType>
434 boost::shared_ptr<NDRegisterAccessor<UserType>> SubdeviceBackend::getSynchronisedRegisterAccessor(
435 const RegisterPath& registerPathName, size_t numberOfWords, size_t wordOffsetInRegister,
436 const AccessModeFlags& flags) {
437 auto info = _registerMap.getBackendRegister(registerPathName);
438
439 auto rawAcc = accessorCreationHelper(info, numberOfWords, wordOffsetInRegister, flags);
440
441 // decorate with appropriate FixedPointConvertingDecorator.
442 if(!flags.has(AccessMode::raw)) {
443 return boost::make_shared<ConvertingDecorator<UserType, int32_t>>(rawAcc, info);
444 }
445
446 if constexpr(std::is_same_v<UserType, int32_t>) {
447 // decorate with appropriate decorator even in raw mode,
448 // so we can properly implement getAsCooked()/setAsCooked().
449 return boost::make_shared<ConvertingRawDecorator<UserType>>(rawAcc, info);
450 }
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 + "')");
453 }
454
455 /********************************************************************************************************************/
456
457 template<typename UserType>
458 boost::shared_ptr<NDRegisterAccessor<UserType>> SubdeviceBackend::getRegisterWindowAccessor(
459 const RegisterPath& registerPathName, size_t numberOfWords, size_t wordOffsetInRegister,
460 const AccessModeFlags& flags) {
461 assert(_type == Type::registerWindow);
463
464 auto info = _registerMap.getBackendRegister(registerPathName);
465 verifyRegisterAccessorSize(info, numberOfWords, wordOffsetInRegister, false);
466
467 boost::shared_ptr<NDRegisterAccessor<UserType>> retVal;
468 callForRawType(info.getDataDescriptor().rawDataType(), [&](auto arg) {
469 using uRawType = std::make_unsigned_t<decltype(arg)>;
470
471 // Determine the "native" data type of the read/write accessors
472 DataType nativeReadWriteType;
473 auto targetCatalogue = _targetDevice->getRegisterCatalogue();
474
475 auto writeDataRegName = _parameters["writeData"];
476 if(writeDataRegName.empty()) {
477 // try the 2reg/3reg name for compatibility
478 writeDataRegName = _parameters["data"];
479 }
480
481 if(!writeDataRegName.empty()) {
482 auto writeAccInfo = targetCatalogue.getRegister(writeDataRegName);
483 nativeReadWriteType = writeAccInfo.getDataDescriptor().minimumDataType();
484 }
485 if(!_parameters["readData"].empty()) {
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"]);
491 }
492 }
493 else {
494 nativeReadWriteType = readAccInfo.getDataDescriptor().minimumDataType();
495 }
496 }
497
498 auto sharedThis = boost::enable_shared_from_this<DeviceBackend>::shared_from_this();
499
500 boost::shared_ptr<NDRegisterAccessor<uRawType>> rawAcc;
501 callForType(nativeReadWriteType, [&](auto rwTypeArg) {
502 if constexpr(std::is_integral_v<decltype(rwTypeArg)>) {
503 rawAcc =
504 boost::make_shared<SubdeviceRegisterWindowAccessor<uRawType, std::make_unsigned_t<decltype(rwTypeArg)>>>(
505 boost::dynamic_pointer_cast<SubdeviceBackend>(sharedThis), registerPathName, numberOfWords,
506 wordOffsetInRegister);
507 }
508 });
509
510 // decorate with appropriate ConvertingDecorator.
511 if(!flags.has(AccessMode::raw)) {
512 retVal = boost::make_shared<ConvertingDecorator<UserType, uRawType>>(rawAcc, info);
513 return;
514 }
515
516 if constexpr(std::is_same_v<UserType, uRawType>) {
517 // decorate with appropriate decorator even in raw mode,
518 // so we can properly implement getAsCooked()/setAsCooked().
519 retVal = boost::make_shared<ConvertingRawDecorator<UserType>>(rawAcc, info);
520 }
521 else if constexpr(std::is_same_v<UserType, std::make_signed_t<uRawType>>) {
522 // Handling for deprecated, signed raw types
523 // As the FixedPointConvertingRawDecorator only works with one template class, we cannot use
524 // the unsigned rawAcc.
525 rawAcc.reset();
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);
533 }
534 });
535
536 retVal = boost::make_shared<ConvertingRawDecorator<UserType>>(signedRawAcc, info);
537 }
538 else {
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 + "')");
542 }
543 });
544
545 return retVal;
546 }
547
548 /********************************************************************************************************************/
549
550 template<>
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);
554
555 // obtain register info
556 auto info = _registerMap.getBackendRegister(registerPathName);
557 verifyRegisterAccessorSize(info, numberOfWords, wordOffsetInRegister, true);
558
559 // store raw flag for later (since we modify the flags)
560 bool isRaw = flags.has(AccessMode::raw);
561
562 // obtain target accessor in raw mode
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);
566
567 // decorate with appropriate FixedPointConvertingDecorator. This is done even
568 // when in raw mode so we can properly implement getAsCooked()/setAsCooked().
569 if(!isRaw) {
570 return boost::make_shared<ConvertingDecorator<int32_t, int32_t>>(rawAcc, info);
571 }
572 return boost::make_shared<ConvertingRawDecorator<int32_t>>(rawAcc, info);
573 }
574
575 /********************************************************************************************************************/
576
577 void SubdeviceBackend::setExceptionImpl() noexcept {
578 obtainTargetBackend();
579 _targetDevice->setException(getActiveExceptionMessage());
580 }
581
582 /********************************************************************************************************************/
583
584 void SubdeviceBackend::activateAsyncRead() noexcept {
585 obtainTargetBackend();
586 _targetDevice->activateAsyncRead();
587 }
588 /********************************************************************************************************************/
589
590 std::set<DeviceBackend::BackendID> SubdeviceBackend::getInvolvedBackendIDs() {
591 obtainTargetBackend();
592 std::set<DeviceBackend::BackendID> retVal{getBackendID()};
593 retVal.merge(_targetDevice->getInvolvedBackendIDs());
594 return retVal;
595 }
596
597} // namespace ChimeraTK
#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.
Definition AccessMode.h:48
bool has(AccessMode flag) const
Check if a certain flag is in the set.
Definition AccessMode.cc:20
void add(AccessMode flag)
Add the given flag to the set.
Definition AccessMode.cc:62
void checkForUnknownFlags(const std::set< AccessMode > &knownFlags) const
Check of any flag which is not in the given set "knownFlags" is set.
Definition AccessMode.cc:32
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 &registerInfo)
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...
Container for backend metadata.
NumericAddressedRegisterInfo getBackendRegister(const RegisterPath &registerPathName) 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 &registerPathName, 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 &registerPathName, 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.
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 &registerPathName, 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 &registerPathName, 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...
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.
Definition Exception.h:51
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)