ChimeraTK-DeviceAccess 03.25.00
Loading...
Searching...
No Matches
NumericAddressedBackendRegisterAccessor.h
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#pragma once
4
11
12#include <ChimeraTK/cppext/finally.hpp>
13
14namespace ChimeraTK {
15
16 template<typename UserType, typename DataConverterType, bool isRaw>
17 class NumericAddressedBackendRegisterAccessor;
18
19 /********************************************************************************************************************/
20
24 template<typename UserType, typename DataConverterType, bool isRaw>
26 public:
27 NumericAddressedBackendRegisterAccessor(const boost::shared_ptr<DeviceBackend>& dev,
28 const RegisterPath& registerPathName, size_t numberOfWords, size_t wordOffsetInRegister, AccessModeFlags flags)
29 : NDRegisterAccessor<UserType>(registerPathName, flags), _dataConverter(registerPathName),
30 _dev(boost::dynamic_pointer_cast<NumericAddressedBackend>(dev)) {
31 // check for unknown flags
33
34 // check device backend
35 _dev = boost::dynamic_pointer_cast<NumericAddressedBackend>(dev);
36 if(!_dev) {
37 throw ChimeraTK::logic_error("NumericAddressedBackendRegisterAccessor is used with a backend which is not "
38 "a NumericAddressedBackend.");
39 }
40
41 // obtain register information
42 _registerInfo = _dev->getRegisterInfo(registerPathName);
43 assert(!_registerInfo.channels.empty());
44
45 if(_registerInfo.elementPitchBits % 8 != 0) {
46 throw ChimeraTK::logic_error("NumericAddressedBackendRegisterAccessor: Elements must be byte aligned.");
47 }
48
49 if(_registerInfo.channels.size() > 1) {
50 throw ChimeraTK::logic_error("NumericAddressedBackendRegisterAccessor is used with a 2D register.");
51 }
52
53 if(_registerInfo.channels.front().bitOffset > 0) {
54 throw ChimeraTK::logic_error("NumericAddressedBackendRegisterAccessor: Registers must be byte aligned.");
55 }
56
57 // check number of words
59 // in void registers we always create one element
60 if(numberOfWords == 0) {
61 numberOfWords = 1;
62 }
63 if(numberOfWords > 1) {
65 "Requested number of words is larger than 1 in VOID register '" + registerPathName + "'!");
66 }
67 if(wordOffsetInRegister > 0) {
68 throw ChimeraTK::logic_error("No offset allowed in VOID register '" + registerPathName + "'!");
69 }
70 }
71 else { // do the regular consistency check
72 if(numberOfWords == 0) {
73 numberOfWords = _registerInfo.getNumberOfElements();
74 }
75 if(numberOfWords + wordOffsetInRegister > _registerInfo.getNumberOfElements()) {
76 throw ChimeraTK::logic_error("Requested number of words (" + std::to_string(numberOfWords) + " + " +
77 std::to_string(wordOffsetInRegister) + ") exceeds the size (" +
78 std::to_string(_registerInfo.getNumberOfElements()) + ") of the register '" + registerPathName + "'!");
79 }
80 if(wordOffsetInRegister >= _registerInfo.getNumberOfElements()) {
81 throw ChimeraTK::logic_error("Requested offset (" + std::to_string(wordOffsetInRegister) +
82 ") exceeds the size (" + std::to_string(_registerInfo.getNumberOfElements()) + ") of the register'" +
83 registerPathName + "'!");
84 }
85 }
86
87 // change registerInfo (local copy!) to account for given offset and length override
88 _registerInfo.address += wordOffsetInRegister * _registerInfo.elementPitchBits / 8;
89 _registerInfo.nElements = numberOfWords;
90
91 // create low-level transfer element handling the actual data transfer to the hardware with raw data
92 assert(_registerInfo.elementPitchBits % 8 == 0);
93 _rawAccessor = boost::make_shared<NumericAddressedLowLevelTransferElement>(
95
96 // allocated the buffers
99
100 // We don't have to fill it in a special way if the accessor is raw
101 // because we have an overloaded, more efficient implementation
102 // in this case. So we can use it in setAsCooked() and getAsCooked()
103 _dataConverter = detail::createDataConverter<DataConverterType>(_registerInfo);
104
105 if(flags.has(AccessMode::raw)) {
106 if(DataType(typeid(UserType)) != _registerInfo.getDataDescriptor().rawDataType()) {
107 throw ChimeraTK::logic_error("Given UserType when obtaining the NumericAddressedBackendRegisterAccessor in "
108 "raw mode does not match the expected type. Use an " +
110 " instead! (Register name: " + registerPathName + "')");
111 }
112 }
113
116 }
117
118 void doReadTransferSynchronously() override { _rawAccessor->readTransfer(); }
119
120 bool doWriteTransfer(ChimeraTK::VersionNumber versionNumber) override {
122 _rawAccessor->writeTransfer(versionNumber);
123 return false;
124 }
125
126 void doPostRead(TransferType type, bool hasNewData) override {
127 if(!_dev->isOpen()) return; // do not delegate if exception was thrown by us in doPreWrite
128
129 _rawAccessor->setActiveException(this->_activeException);
130 _rawAccessor->postRead(type, hasNewData);
131
132 if(!hasNewData) return;
133
134 if constexpr(!isRaw || std::is_same<UserType, std::string>::value) {
136 typedef decltype(t) RawType;
137 auto itsrc = (RawType*)_rawAccessor->begin(_registerInfo.address);
138 _dataConverter.template vectorToCooked<UserType>(itsrc, itsrc + buffer_2D[0].size(), buffer_2D[0].begin());
139 });
140 }
141 else {
142 // optimised variant for raw transfers (unless type is a string)
143 auto* itsrc = _rawAccessor->begin(_registerInfo.address);
144 auto* itdst = buffer_2D[0].data();
145 memcpy(itdst, itsrc, buffer_2D[0].size() * sizeof(UserType));
146 }
147
148 // we don't put the setting of the version number into the PrePostActionImplementor
149 // because it does not need template specialisation, and the implementer does not
150 // know about _versionNumber. It's just easier here.
151 this->_versionNumber = _rawAccessor->getVersionNumber();
152 this->_dataValidity = _rawAccessor->dataValidity();
153 }
154
155 void doPreWrite(TransferType type, VersionNumber versionNumber) override {
156 if(!_dev->isOpen()) throw ChimeraTK::logic_error("Device not opened.");
157 // raw accessor preWrite must be called before our _prePostActionsImplementor.doPreWrite(), as it needs to
158 // prepare the buffer in case of unaligned access and acquire the lock.
159 _rawAccessor->preWrite(type, versionNumber);
160
161 if constexpr(!isRaw || std::is_same<UserType, std::string>::value) {
164 "NumericAddressedBackend: Writing to a non-writeable register is not allowed (Register name: " +
166 }
168 typedef decltype(t) RawType;
169 auto itsrc = (RawType*)_rawAccessor->begin(_registerInfo.address);
170 for(auto itdst = buffer_2D[0].begin(); itdst != buffer_2D[0].end(); ++itdst) {
171 *itsrc = _dataConverter.template toRaw<UserType>(*itdst);
172 ++itsrc;
173 }
174 });
175 }
176 else {
177 // optimised variant for raw transfers (unless type is a string)
178 auto* itdst = _rawAccessor->begin(_registerInfo.address);
179 auto itsrc = buffer_2D[0].begin();
180 memcpy(&(*itdst), &(*itsrc), buffer_2D[0].size() * sizeof(UserType));
181 }
182
183 _rawAccessor->setDataValidity(this->_dataValidity);
184 }
185
186 void doPreRead(TransferType type) override {
187 if(!_dev->isOpen()) throw ChimeraTK::logic_error("Device not opened.");
188 if(!_registerInfo.isReadable()) {
190 "NumericAddressedBackend: Reading from a non-readable register is not allowed (Register name: " +
191 _registerInfo.getRegisterName() + ").");
192 }
193 _rawAccessor->preRead(type);
194 }
195
196 void doPostWrite(TransferType type, VersionNumber versionNumber) override {
197 if(!_dev->isOpen()) return; // do not delegate if exception was thrown by us in doPreWrite
198 _rawAccessor->setActiveException(this->_activeException);
199 _rawAccessor->postWrite(type, versionNumber);
200 }
201
202 [[nodiscard]] bool mayReplaceOther(const boost::shared_ptr<TransferElement const>& other) const override {
203 auto rhsCasted = boost::dynamic_pointer_cast<
205 if(rhsCasted.get() == this) {
206 return false;
207 }
208 if(!rhsCasted) return false;
209 if(_dev != rhsCasted->_dev) return false;
210 if(_registerInfo != rhsCasted->_registerInfo) return false;
211 if(_dataConverter != rhsCasted->_dataConverter) return false;
212 return true;
213 }
214
215 [[nodiscard]] bool isReadOnly() const override { return isReadable() && !isWriteable(); }
216
217 [[nodiscard]] bool isReadable() const override { return _registerInfo.isReadable(); }
218
219 [[nodiscard]] bool isWriteable() const override { return _registerInfo.isWriteable(); }
220
221 template<typename COOKED_TYPE>
222 COOKED_TYPE getAsCooked_impl(unsigned int channel, unsigned int sample);
223
224 template<typename COOKED_TYPE>
225 void setAsCooked_impl(unsigned int channel, unsigned int sample, COOKED_TYPE value);
226
227 // a local typename so the DEFINE_VIRTUAL_FUNCTION_TEMPLATE_VTABLE_FILLER does
228 // not get confused by the comma which separates the two template parameters
230
233
234 void setExceptionBackend(boost::shared_ptr<DeviceBackend> exceptionBackend) override {
235 this->_exceptionBackend = exceptionBackend;
236 _rawAccessor->setExceptionBackend(exceptionBackend);
237 }
238
239 protected:
243
245 DataConverterType _dataConverter;
246
248 boost::shared_ptr<NumericAddressedLowLevelTransferElement> _rawAccessor;
249
251 boost::shared_ptr<NumericAddressedBackend> _dev;
252
253 std::vector<boost::shared_ptr<TransferElement>> getHardwareAccessingElements() override {
254 return _rawAccessor->getHardwareAccessingElements();
255 }
256
257 std::list<boost::shared_ptr<TransferElement>> getInternalElements() override {
258 return {_rawAccessor}; // the rawAccessor always returns an empty list
259 }
260
261 void replaceTransferElement(boost::shared_ptr<TransferElement> newElement) override {
262 auto casted = boost::dynamic_pointer_cast<NumericAddressedLowLevelTransferElement>(newElement);
263 if(casted && casted->isMergeable(_rawAccessor)) {
264 size_t newStartAddress = std::min(casted->_startAddress, _rawAccessor->_startAddress);
265 size_t newStopAddress = std::max(
266 casted->_startAddress + casted->_numberOfBytes, _rawAccessor->_startAddress + _rawAccessor->_numberOfBytes);
267 size_t newNumberOfBytes = newStopAddress - newStartAddress;
268 casted->changeAddress(newStartAddress, newNumberOfBytes);
269 _rawAccessor = casted;
270 }
271 _rawAccessor->setExceptionBackend(this->_exceptionBackend);
272 }
273
278 template<typename RawT, typename CookedT>
280 static CookedT vectorToCooked(DataConverterType&, const typename std::vector<RawT>::const_iterator&,
281 const typename std::vector<RawT>::const_iterator&, const typename std::vector<CookedT>::iterator&) {
282 throw ChimeraTK::logic_error("Getting as cooked is only available for raw accessors!");
283 }
284 static RawT toRaw(DataConverterType&, CookedT&) {
285 throw ChimeraTK::logic_error("Setting as cooked is only available for raw accessors!");
286 }
287 };
288 template<typename CookedT>
290 static void vectorToCooked(DataConverterType& dataConverter,
291 const typename std::vector<int8_t>::const_iterator& start,
292 const typename std::vector<int8_t>::const_iterator& end,
293 const typename std::vector<CookedT>::iterator& cooked) {
294 dataConverter.template vectorToCooked<CookedT>(start, end, cooked);
295 }
296 static int8_t toRaw(DataConverterType& dataConverter, CookedT& value) { return dataConverter.toRaw(value); }
297 };
298 template<typename CookedT>
300 static void vectorToCooked(DataConverterType& dataConverter,
301 const typename std::vector<int16_t>::const_iterator& start,
302 const typename std::vector<int16_t>::const_iterator& end,
303 const typename std::vector<CookedT>::iterator& cooked) {
304 dataConverter.template vectorToCooked<CookedT>(start, end, cooked);
305 }
306 static int16_t toRaw(DataConverterType& dataConverter, CookedT& value) { return dataConverter.toRaw(value); }
307 };
308 template<typename CookedT>
310 static void vectorToCooked(DataConverterType& dataConverter,
311 const typename std::vector<int32_t>::const_iterator& start,
312 const typename std::vector<int32_t>::const_iterator& end,
313 const typename std::vector<CookedT>::iterator& cooked) {
314 dataConverter.template vectorToCooked<CookedT>(start, end, cooked);
315 }
316 static int32_t toRaw(DataConverterType& dataConverter, CookedT& value) { return dataConverter.toRaw(value); }
317 };
318
319 using NDRegisterAccessor<UserType>::buffer_2D;
320 }; // namespace ChimeraTK
321
323
324 template<typename UserType, typename DataConverterType, bool isRaw>
325 template<typename COOKED_TYPE>
327 unsigned int channel, unsigned int sample) {
328 if(isRaw) {
329 std::vector<COOKED_TYPE> cookedData(1);
331 NDRegisterAccessor<UserType>::buffer_2D[channel].begin() + sample,
332 NDRegisterAccessor<UserType>::buffer_2D[channel].begin() + sample + 1, cookedData.begin());
333 return cookedData[0];
334 }
335 throw ChimeraTK::logic_error("Getting as cooked is only available for raw accessors!");
336 }
337
339
340 template<typename UserType, typename DataConverterType, bool isRaw>
341 template<typename COOKED_TYPE>
343 unsigned int channel, unsigned int sample, COOKED_TYPE value) {
344 if(isRaw) {
347 }
348 else {
349 throw ChimeraTK::logic_error("Setting as cooked is only available for raw accessors!");
350 }
351 }
352
354
363
364} // namespace ChimeraTK
#define DECLARE_MULTI_TEMPLATE_FOR_CHIMERATK_USER_TYPES(TemplateClass,...)
Macro to declare a template class with multiple template parameters for all supported user types.
#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 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
DataType rawDataType() const
Get the raw data type.
A class to describe which of the supported data types is used.
std::string getAsString() const
Return string representation of the data type.
The fixed point converter provides conversion functions between a user type and up to 32 bit fixed po...
N-dimensional register accessor.
std::vector< std::vector< UserType > > buffer_2D
Buffer of converted data elements.
Base class for address-based device backends (e.g.
Implementation of the NDRegisterAccessor for NumericAddressedBackends for scalar and 1D registers.
std::vector< boost::shared_ptr< TransferElement > > getHardwareAccessingElements() override
Obtain the underlying TransferElements with actual hardware access.
bool mayReplaceOther(const boost::shared_ptr< TransferElement const > &other) const override
Check whether the TransferElement can be used in places where the TransferElement "other" is currentl...
NumericAddressedRegisterInfo _registerInfo
Address, size and fixed-point representation information of the register from the map file.
COOKED_TYPE getAsCooked_impl(unsigned int channel, unsigned int sample)
DEFINE_VIRTUAL_FUNCTION_TEMPLATE_VTABLE_FILLER(THIS_TYPE, getAsCooked_impl, 2)
void doReadTransferSynchronously() override
Implementation version of readTransfer() for synchronous reads.
NumericAddressedBackendRegisterAccessor(const boost::shared_ptr< DeviceBackend > &dev, const RegisterPath &registerPathName, size_t numberOfWords, size_t wordOffsetInRegister, AccessModeFlags flags)
bool doWriteTransfer(ChimeraTK::VersionNumber versionNumber) override
Implementation version of writeTransfer().
void doPreWrite(TransferType type, VersionNumber versionNumber) override
Backend specific implementation of preWrite().
void doPostWrite(TransferType type, VersionNumber versionNumber) override
Backend specific implementation of postWrite().
void doPreRead(TransferType type) override
Backend specific implementation of preRead().
DataConverterType _dataConverter
Converter to interpret the data.
void setExceptionBackend(boost::shared_ptr< DeviceBackend > exceptionBackend) override
Set the backend to which the exception has to be reported.
bool isReadOnly() const override
Check if transfer element is read only, i.e.
std::list< boost::shared_ptr< TransferElement > > getInternalElements() override
Obtain the full list of TransferElements internally used by this TransferElement.
void setAsCooked_impl(unsigned int channel, unsigned int sample, COOKED_TYPE value)
boost::shared_ptr< NumericAddressedBackend > _dev
the backend to use for the actual hardware access
boost::shared_ptr< NumericAddressedLowLevelTransferElement > _rawAccessor
raw accessor
DEFINE_VIRTUAL_FUNCTION_TEMPLATE_VTABLE_FILLER(THIS_TYPE, setAsCooked_impl, 3)
void replaceTransferElement(boost::shared_ptr< TransferElement > newElement) override
Search for all underlying TransferElements which are considered identical (see sameRegister()) with t...
bool isReadable() const override
Check if transfer element is readable.
bool isWriteable() const override
Check if transfer element is writeable.
void doPostRead(TransferType type, bool hasNewData) override
Backend specific implementation of postRead().
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)
const DataDescriptor & getDataDescriptor() const override
Return description of the actual payload data for this register.
uint32_t elementPitchBits
Distance in bits (!) between two elements (of the same channel)
uint64_t address
Lower part of the address relative to BAR, in bytes.
RegisterPath getRegisterName() const override
Return full path name of the register (including modules)
bool isWriteable() const override
Return whether the register is writeable.
unsigned int getNumberOfElements() const override
Return number of elements per channel.
Class to store a register path name.
DataValidity _dataValidity
The validity of the data in the application buffer.
std::exception_ptr _activeException
Exception to be rethrown in postXXX() in case hasSeenException == true Can be set via setActiveExcept...
VersionNumber _versionNumber
The version number of the last successful transfer.
bool _isInTransferGroup
Flag whether this TransferElement has been added to a TransferGroup or not.
Class for generating and holding version numbers without exposing a numeric representation.
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...
@ raw
Raw access: disable any possible conversion from the original hardware data type into the given UserT...
TransferType
Used to indicate the applicable operation on a Transferelement.
std::string to_string(const std::string &v)
Needs to have the same interface as FixedPointConverter, except for the constructor.
static void vectorToCooked(DataConverterType &dataConverter, const typename std::vector< int8_t >::const_iterator &start, const typename std::vector< int8_t >::const_iterator &end, const typename std::vector< CookedT >::iterator &cooked)
static void vectorToCooked(DataConverterType &dataConverter, const typename std::vector< int16_t >::const_iterator &start, const typename std::vector< int16_t >::const_iterator &end, const typename std::vector< CookedT >::iterator &cooked)
static void vectorToCooked(DataConverterType &dataConverter, const typename std::vector< int32_t >::const_iterator &start, const typename std::vector< int32_t >::const_iterator &end, const typename std::vector< CookedT >::iterator &cooked)
static CookedT vectorToCooked(DataConverterType &, const typename std::vector< RawT >::const_iterator &, const typename std::vector< RawT >::const_iterator &, const typename std::vector< CookedT >::iterator &)