ChimeraTK-DeviceAccess 03.31.00
Loading...
Searching...
No Matches
NumericAddressedBackendRegisterAccessor.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
5
6namespace ChimeraTK {
7
8 /********************************************************************************************************************/
9
10 template<typename UserType, bool isRaw>
12 const boost::shared_ptr<DeviceBackend>& dev, const RegisterPath& registerPathName, size_t numberOfWords,
13 size_t wordOffsetInRegister, AccessModeFlags flags)
14 : NDRegisterAccessor<UserType>(registerPathName, flags),
15 _dev(boost::dynamic_pointer_cast<NumericAddressedBackend>(dev)) {
16 // check for unknown flags
18
19 // check device backend
20 _dev = boost::dynamic_pointer_cast<NumericAddressedBackend>(dev);
21 if(!_dev) {
22 throw ChimeraTK::logic_error("NumericAddressedBackendRegisterAccessor is used with a backend which is not "
23 "a NumericAddressedBackend.");
24 }
25
26 // obtain register information
27 _registerInfo = _dev->getRegisterInfo(registerPathName);
28 assert(!_registerInfo.channels.empty());
29
30 // Propagate the engineering unit and description from the register info into the accessor
33
34 if(_registerInfo.elementPitchBits % 8 != 0) {
35 throw ChimeraTK::logic_error("NumericAddressedBackendRegisterAccessor: Elements must be byte aligned.");
36 }
37
38 if(_registerInfo.channels.size() > 1) {
39 throw ChimeraTK::logic_error("NumericAddressedBackendRegisterAccessor is used with a 2D register.");
40 }
41
42 if(_registerInfo.channels.front().bitOffset > 0) {
43 throw ChimeraTK::logic_error("NumericAddressedBackendRegisterAccessor: Registers must be byte aligned.");
44 }
45
46 // check number of words
48 // in void registers we always create one element
49 if(numberOfWords == 0) {
50 numberOfWords = 1;
51 }
52 if(numberOfWords > 1) {
54 "Requested number of words is larger than 1 in VOID register '" + registerPathName + "'!");
55 }
56 if(wordOffsetInRegister > 0) {
57 throw ChimeraTK::logic_error("No offset allowed in VOID register '" + registerPathName + "'!");
58 }
59 }
60 else { // do the regular consistency check
61 if(numberOfWords == 0) {
62 numberOfWords = _registerInfo.getNumberOfElements();
63 }
64 if(numberOfWords + wordOffsetInRegister > _registerInfo.getNumberOfElements()) {
65 throw ChimeraTK::logic_error("Requested number of words (" + std::to_string(numberOfWords) + " + " +
66 std::to_string(wordOffsetInRegister) + ") exceeds the size (" +
67 std::to_string(_registerInfo.getNumberOfElements()) + ") of the register '" + registerPathName + "'!");
68 }
69 if(wordOffsetInRegister >= _registerInfo.getNumberOfElements()) {
70 throw ChimeraTK::logic_error("Requested offset (" + std::to_string(wordOffsetInRegister) +
71 ") exceeds the size (" + std::to_string(_registerInfo.getNumberOfElements()) + ") of the register'" +
72 registerPathName + "'!");
73 }
74 }
75
76 // change registerInfo (local copy!) to account for given offset and length override
77 _registerInfo.address += wordOffsetInRegister * _registerInfo.elementPitchBits / 8;
78 _registerInfo.nElements = numberOfWords;
79
80 // create low-level transfer element handling the actual data transfer to the hardware with raw data
81 assert(_registerInfo.elementPitchBits % 8 == 0);
82 _rawAccessor = boost::make_shared<NumericAddressedLowLevelTransferElement>(
84
85 // allocated the buffers
88
89 if constexpr(!isRaw) {
91 RawConverter::ConverterLoopHelper::makeConverterLoopHelper<UserType>(_registerInfo, 0, 0, *this);
92 }
93
94 // Register this read consumer with the raw element's read coordinator. Registration is unconditional and assumes no
95 // property of the register (in particular not its read-onlyness); the coordinator serves every read consumer. The
96 // accessor's own converter loop and raw copy serve the write path only.
98
99 if(flags.has(AccessMode::raw)) {
100 if(DataType(typeid(UserType)) != _registerInfo.getDataDescriptor().rawDataType()) {
101 throw ChimeraTK::logic_error("Given UserType when obtaining the NumericAddressedBackendRegisterAccessor in "
102 "raw mode does not match the expected type. Use an " +
104 " instead! (Register name: " + registerPathName + "')");
105 }
106 }
107
110 }
111
112 /********************************************************************************************************************/
113
114 template<typename UserType, bool isRaw>
118
119 /********************************************************************************************************************/
120
121 template<typename UserType, bool isRaw>
123 ChimeraTK::VersionNumber versionNumber) {
125 _rawAccessor->writeTransfer(versionNumber);
126 return false;
127 }
128
129 /********************************************************************************************************************/
130
131 template<typename UserType, bool isRaw>
133 if(!_dev->isOpen()) {
134 // do not delegate if exception was thrown by us in doPreWrite
135 return;
136 }
137
138 _rawAccessor->setActiveException(this->_activeException);
139 _rawAccessor->postRead(type, hasNewData);
140
141 if(!hasNewData) {
142 return;
143 }
144
145 // The conversion has already been performed by the raw element's read coordinator into the staging buffer; swap it
146 // into the application buffer. Data and meta data are updated together below, and the buffers stay unchanged when
147 // hasNewData is false.
148 buffer_2D[0].swap(_readRegistration.staging());
149
150 // we don't put the setting of the version number into the PrePostActionImplementor
151 // because it does not need template specialisation, and the implementer does not
152 // know about _versionNumber. It's just easier here.
153 this->_versionNumber = _rawAccessor->getVersionNumber();
154 this->_dataValidity = _rawAccessor->dataValidity();
155 }
156
157 /********************************************************************************************************************/
158
159 template<typename UserType, bool isRaw>
160 template<class CookedType, typename RawType, RawConverter::SignificantBitsCase sc, RawConverter::FractionalCase fc,
161 bool isSigned>
163 RawConverter::Converter<CookedType, RawType, sc, fc, isSigned> converter, [[maybe_unused]] size_t implParameter) {
164 static_assert(std::is_same_v<UserType, CookedType>);
165 if constexpr(!isRaw) {
166 if constexpr(!std::is_same_v<RawType, ChimeraTK::Void>) {
167 auto* begin = _rawAccessor->begin(_registerInfo.address);
168 assert(begin != nullptr);
169 // the elements are strided by elementPitchBits, which may exceed sizeof(RawType) for a channel slice
170 const size_t stride = _registerInfo.elementPitchBits / 8;
171 for(auto [itsrc, itdst] = std::make_pair(begin, buffer_2D[0].begin()); itdst != buffer_2D[0].end();
172 itsrc += stride, ++itdst) {
173 RawType temp;
174 memcpy(&temp, itsrc, sizeof(RawType));
175 *itdst = converter.toCooked(temp);
176 }
177 }
178 }
179 else {
180 // This function will never be called for raw accessors
181 assert(false);
182 }
183 }
184
185 /********************************************************************************************************************/
186
187 template<typename UserType, bool isRaw>
189 TransferType type, VersionNumber versionNumber) {
190 if(!_dev->isOpen()) {
191 throw ChimeraTK::logic_error("Device not opened.");
192 }
193 // raw accessor preWrite must be called before our _prePostActionsImplementor.doPreWrite(), as it needs to
194 // prepare the buffer in case of unaligned access and acquire the lock.
195 _rawAccessor->preWrite(type, versionNumber);
196
197 if constexpr(!isRaw || std::is_same<UserType, std::string>::value) {
198 if(!_registerInfo.isWriteable()) {
200 "NumericAddressedBackend: Writing to a non-writeable register is not allowed (Register name: " +
201 _registerInfo.getRegisterName() + ").");
202 }
203 _converterLoopHelper->doPreWrite();
204 }
205 else {
206 // optimised variant for raw transfers (unless type is a string)
207 // the elements are strided by elementPitchBits, which may exceed sizeof(UserType) for a channel slice
208 auto* itdst = _rawAccessor->begin(_registerInfo.address);
209 auto* itsrc = buffer_2D[0].data();
210 const size_t stride = _registerInfo.elementPitchBits / 8;
211 for(size_t i = 0; i < buffer_2D[0].size(); ++i) {
212 memcpy(itdst + i * stride, itsrc + i, sizeof(UserType));
213 }
214 }
215
216 _rawAccessor->setDataValidity(this->_dataValidity);
217 }
218
219 /********************************************************************************************************************/
220
221 template<typename UserType, bool isRaw>
222 template<class CookedType, typename RawType, RawConverter::SignificantBitsCase sc, RawConverter::FractionalCase fc,
223 bool isSigned>
225 RawConverter::Converter<CookedType, RawType, sc, fc, isSigned> converter, [[maybe_unused]] size_t implParameter) {
226 static_assert(std::is_same_v<UserType, CookedType>);
227 if constexpr(!isRaw) {
228 if constexpr(!std::is_same_v<RawType, ChimeraTK::Void>) {
229 auto* begin = _rawAccessor->begin(_registerInfo.address);
230 // the elements are strided by elementPitchBits, which may exceed sizeof(RawType) for a channel slice
231 const size_t stride = _registerInfo.elementPitchBits / 8;
232 for(auto [itsrc, itdst] = std::make_pair(buffer_2D[0].begin(), begin); itsrc != buffer_2D[0].end();
233 ++itsrc, itdst += stride) {
234 RawType temp = converter.toRaw(*itsrc);
235 memcpy(itdst, &temp, sizeof(RawType));
236 }
237 }
238 }
239 else {
240 // This function will never be called for raw accessors.
241 assert(false);
242 }
243 }
244
245 /********************************************************************************************************************/
246
247 template<typename UserType, bool isRaw>
249 if(!_dev->isOpen()) {
250 throw ChimeraTK::logic_error("Device not opened.");
251 }
252 if(!_registerInfo.isReadable()) {
254 "NumericAddressedBackend: Reading from a non-readable register is not allowed (Register name: " +
255 _registerInfo.getRegisterName() + ").");
256 }
257 _rawAccessor->preRead(type);
258 }
259
260 /********************************************************************************************************************/
261
262 template<typename UserType, bool isRaw>
264 TransferType type, VersionNumber versionNumber) {
265 if(!_dev->isOpen()) {
266 // do not delegate if exception was thrown by us in doPreWrite
267 return;
268 }
269 _rawAccessor->setActiveException(this->_activeException);
270 _rawAccessor->postWrite(type, versionNumber);
271 }
272
273 /********************************************************************************************************************/
274
275 template<typename UserType, bool isRaw>
277 const boost::shared_ptr<TransferElement const>& other) const {
278 auto rhsCasted = boost::dynamic_pointer_cast<const NumericAddressedBackendRegisterAccessor<UserType, isRaw>>(other);
279 if(rhsCasted.get() == this) {
280 return false;
281 }
282 if(!rhsCasted) {
283 return false;
284 }
285 if(_dev != rhsCasted->_dev) {
286 return false;
287 }
288 if(_registerInfo != rhsCasted->_registerInfo) {
289 return false;
290 }
291 // No need to compare the RawConverters, since they are based on the registerInfo and UserType only.
292 return true;
293 }
294
295 /********************************************************************************************************************/
296
297 template<typename UserType, bool isRaw>
299 return isReadable() && !isWriteable();
300 }
301
302 /********************************************************************************************************************/
303
304 template<typename UserType, bool isRaw>
306 return _registerInfo.isReadable();
307 }
308
309 /********************************************************************************************************************/
310
311 template<typename UserType, bool isRaw>
313 return _registerInfo.isWriteable();
314 }
315
316 /********************************************************************************************************************/
317
318 template<typename UserType, bool isRaw>
320 boost::shared_ptr<DeviceBackend> exceptionBackend) {
321 this->_exceptionBackend = exceptionBackend;
322 _rawAccessor->setExceptionBackend(exceptionBackend);
323 }
324
325 /********************************************************************************************************************/
326
327 template<typename UserType, bool isRaw>
329 _readRegistration = detail::ReadCoordinator::Registration<UserType>(&_rawAccessor->_readCoordinator, _registerInfo,
330 isRaw, _registerInfo.address - _rawAccessor->_startAddress, _registerInfo.elementPitchBits / 8,
331 _registerInfo.nElements);
332 }
333
334 /********************************************************************************************************************/
335
336 template<typename UserType, bool isRaw>
337 std::vector<boost::shared_ptr<TransferElement>> NumericAddressedBackendRegisterAccessor<UserType,
338 isRaw>::getHardwareAccessingElements() {
339 return _rawAccessor->getHardwareAccessingElements();
340 }
341
342 /********************************************************************************************************************/
343
344 template<typename UserType, bool isRaw>
345 std::list<boost::shared_ptr<TransferElement>> NumericAddressedBackendRegisterAccessor<UserType,
346 isRaw>::getInternalElements() {
347 return {_rawAccessor}; // the rawAccessor always returns an empty list
348 }
349
350 /********************************************************************************************************************/
351
352 template<typename UserType, bool isRaw>
354 boost::shared_ptr<TransferElement> newElement) {
355 auto casted = boost::dynamic_pointer_cast<NumericAddressedLowLevelTransferElement>(newElement);
356 if(casted && casted->isMergeable(_rawAccessor)) {
357 size_t newStartAddress = std::min(casted->_startAddress, _rawAccessor->_startAddress);
358 size_t newStopAddress = std::max(
359 casted->_startAddress + casted->_numberOfBytes, _rawAccessor->_startAddress + _rawAccessor->_numberOfBytes);
360 size_t newNumberOfBytes = newStopAddress - newStartAddress;
361 // Unregister before the old element loses its last reference below, then re-register with the merged element,
362 // which has a different start address.
363 _readRegistration.reset();
364 casted->changeAddress(newStartAddress, newNumberOfBytes);
365 _rawAccessor = casted;
366 registerWithReadCoordinator();
367 }
368 _rawAccessor->setExceptionBackend(this->_exceptionBackend);
369 }
370
371 /********************************************************************************************************************/
372
373 template<typename UserType, bool isRaw>
374 template<typename COOKED_TYPE>
376 unsigned int channel, unsigned int sample) {
377 if constexpr(isRaw && std::is_integral_v<UserType>) {
378 if constexpr(isRawType<std::make_unsigned_t<UserType>>) {
379 // Note: the "UserType" is our RawType here!
380 COOKED_TYPE rv;
381 RawConverter::withConverter<COOKED_TYPE, std::make_unsigned_t<UserType>>(_registerInfo, 0, [&](auto converter) {
382 rv = converter.toCooked(
383 std::make_unsigned_t<UserType>(NDRegisterAccessor<UserType>::buffer_2D[channel][sample]));
384 });
385 return rv;
386 }
387 }
388 throw ChimeraTK::logic_error("Getting as cooked is only available for raw accessors!");
389 }
390
391 /********************************************************************************************************************/
392
393 template<typename UserType, bool isRaw>
394 template<typename COOKED_TYPE>
396 unsigned int channel, unsigned int sample, COOKED_TYPE value) {
397 if constexpr(isRaw && std::is_integral_v<UserType>) {
398 if constexpr(isRawType<std::make_unsigned_t<UserType>>) {
399 // Note: the "UserType" is our RawType here!
400 RawConverter::withConverter<COOKED_TYPE, std::make_unsigned_t<UserType>>(_registerInfo, 0, [&](auto converter) {
401 NDRegisterAccessor<UserType>::buffer_2D[channel][sample] = UserType(converter.toRaw(value));
402 });
403 }
404 return;
405 }
406 throw ChimeraTK::logic_error("Setting as cooked is only available for raw accessors!");
407 }
408
409 /********************************************************************************************************************/
410
413
414 /********************************************************************************************************************/
415
416} /* namespace ChimeraTK */
#define INSTANTIATE_MULTI_TEMPLATE_FOR_CHIMERATK_USER_TYPES(TemplateClass,...)
#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.
N-dimensional register accessor.
Base class for address-based device backends (e.g.
Implementation of the NDRegisterAccessor for NumericAddressedBackends for scalar and 1D registers.
void doPostWrite(TransferType type, VersionNumber versionNumber) override
Backend specific implementation of postWrite().
std::vector< boost::shared_ptr< TransferElement > > getHardwareAccessingElements() override
Obtain the underlying TransferElements with actual hardware access.
void doPreWriteImpl(RawConverter::Converter< CookedType, RawType, sc, fc, isSigned > converter, size_t implParameter)
void doPostReadImpl(RawConverter::Converter< CookedType, RawType, sc, fc, isSigned > converter, size_t implParameter)
bool isReadOnly() const override
Check if transfer element is read only, i.e.
void setExceptionBackend(boost::shared_ptr< DeviceBackend > exceptionBackend) override
Set the backend to which the exception has to be reported.
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...
void doPostRead(TransferType type, bool hasNewData) override
Backend specific implementation of postRead().
bool doWriteTransfer(ChimeraTK::VersionNumber versionNumber) override
Implementation version of writeTransfer().
std::unique_ptr< RawConverter::ConverterLoopHelper > _converterLoopHelper
Converter to interpret the data.
void registerWithReadCoordinator()
(Re-)register this read consumer with the read coordinator of the current raw element.
void setAsCooked_impl(unsigned int channel, unsigned int sample, COOKED_TYPE value)
bool isWriteable() const override
Check if transfer element is writeable.
void doPreRead(TransferType type) override
Backend specific implementation of preRead().
void doReadTransferSynchronously() override
Implementation version of readTransfer() for synchronous reads.
void replaceTransferElement(boost::shared_ptr< TransferElement > newElement) override
Search for all underlying TransferElements which are considered identical (see sameRegister()) with t...
boost::shared_ptr< NumericAddressedBackend > _dev
the backend to use for the actual hardware access
NumericAddressedRegisterInfo _registerInfo
Address, size and fixed-point representation information of the register from the map file.
boost::shared_ptr< NumericAddressedLowLevelTransferElement > _rawAccessor
raw accessor
COOKED_TYPE getAsCooked_impl(unsigned int channel, unsigned int sample)
void doPreWrite(TransferType type, VersionNumber versionNumber) override
Backend specific implementation of preWrite().
bool isReadable() const override
Check if transfer element is readable.
NumericAddressedBackendRegisterAccessor(const boost::shared_ptr< DeviceBackend > &dev, const RegisterPath &registerPathName, size_t numberOfWords, size_t wordOffsetInRegister, AccessModeFlags flags)
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.
unsigned int getNumberOfElements() const override
Return number of elements per channel.
Converter class for conversions from raw to cooked values.
UserType toCooked(RawType rawValue)
RawType toRaw(UserType cookedValue)
Class to store a register path name.
std::string _description
Description of this variable/register.
std::string _unit
Engineering unit.
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
This header collects some conversion routines which should be useful both for ApplicationCore Python ...
@ 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)