ChimeraTK-DeviceAccess 03.26.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 if(_registerInfo.elementPitchBits % 8 != 0) {
31 throw ChimeraTK::logic_error("NumericAddressedBackendRegisterAccessor: Elements must be byte aligned.");
32 }
33
34 if(_registerInfo.channels.size() > 1) {
35 throw ChimeraTK::logic_error("NumericAddressedBackendRegisterAccessor is used with a 2D register.");
36 }
37
38 if(_registerInfo.channels.front().bitOffset > 0) {
39 throw ChimeraTK::logic_error("NumericAddressedBackendRegisterAccessor: Registers must be byte aligned.");
40 }
41
42 // check number of words
44 // in void registers we always create one element
45 if(numberOfWords == 0) {
46 numberOfWords = 1;
47 }
48 if(numberOfWords > 1) {
50 "Requested number of words is larger than 1 in VOID register '" + registerPathName + "'!");
51 }
52 if(wordOffsetInRegister > 0) {
53 throw ChimeraTK::logic_error("No offset allowed in VOID register '" + registerPathName + "'!");
54 }
55 }
56 else { // do the regular consistency check
57 if(numberOfWords == 0) {
58 numberOfWords = _registerInfo.getNumberOfElements();
59 }
60 if(numberOfWords + wordOffsetInRegister > _registerInfo.getNumberOfElements()) {
61 throw ChimeraTK::logic_error("Requested number of words (" + std::to_string(numberOfWords) + " + " +
62 std::to_string(wordOffsetInRegister) + ") exceeds the size (" +
63 std::to_string(_registerInfo.getNumberOfElements()) + ") of the register '" + registerPathName + "'!");
64 }
65 if(wordOffsetInRegister >= _registerInfo.getNumberOfElements()) {
66 throw ChimeraTK::logic_error("Requested offset (" + std::to_string(wordOffsetInRegister) +
67 ") exceeds the size (" + std::to_string(_registerInfo.getNumberOfElements()) + ") of the register'" +
68 registerPathName + "'!");
69 }
70 }
71
72 // change registerInfo (local copy!) to account for given offset and length override
73 _registerInfo.address += wordOffsetInRegister * _registerInfo.elementPitchBits / 8;
74 _registerInfo.nElements = numberOfWords;
75
76 // create low-level transfer element handling the actual data transfer to the hardware with raw data
77 assert(_registerInfo.elementPitchBits % 8 == 0);
78 _rawAccessor = boost::make_shared<NumericAddressedLowLevelTransferElement>(
80
81 // allocated the buffers
84
85 if constexpr(!isRaw) {
87 RawConverter::ConverterLoopHelper::makeConverterLoopHelper<UserType>(_registerInfo, 0, *this);
88 }
89
90 if(flags.has(AccessMode::raw)) {
91 if(DataType(typeid(UserType)) != _registerInfo.getDataDescriptor().rawDataType()) {
92 throw ChimeraTK::logic_error("Given UserType when obtaining the NumericAddressedBackendRegisterAccessor in "
93 "raw mode does not match the expected type. Use an " +
95 " instead! (Register name: " + registerPathName + "')");
96 }
97 }
98
101 }
102
103 /********************************************************************************************************************/
104
105 template<typename UserType, bool isRaw>
109
110 /********************************************************************************************************************/
111
112 template<typename UserType, bool isRaw>
114 ChimeraTK::VersionNumber versionNumber) {
116 _rawAccessor->writeTransfer(versionNumber);
117 return false;
118 }
119
120 /********************************************************************************************************************/
121
122 template<typename UserType, bool isRaw>
124 if(!_dev->isOpen()) return; // do not delegate if exception was thrown by us in doPreWrite
125
126 _rawAccessor->setActiveException(this->_activeException);
127 _rawAccessor->postRead(type, hasNewData);
128
129 if(!hasNewData) return;
130
131 if constexpr(!isRaw || std::is_same<UserType, std::string>::value) {
132 _converterLoopHelper->doPostRead();
133 }
134 else {
135 // optimised variant for raw transfers (unless type is a string)
136 auto* itsrc = _rawAccessor->begin(_registerInfo.address);
137 auto* itdst = buffer_2D[0].data();
138 memcpy(itdst, itsrc, buffer_2D[0].size() * sizeof(UserType));
139 }
140
141 // we don't put the setting of the version number into the PrePostActionImplementor
142 // because it does not need template specialisation, and the implementer does not
143 // know about _versionNumber. It's just easier here.
144 this->_versionNumber = _rawAccessor->getVersionNumber();
145 this->_dataValidity = _rawAccessor->dataValidity();
146 }
147
148 /********************************************************************************************************************/
149
150 template<typename UserType, bool isRaw>
151 template<class CookedType, typename RawType, RawConverter::SignificantBitsCase sc, RawConverter::FractionalCase fc,
152 bool isSigned>
154 RawConverter::Converter<CookedType, RawType, sc, fc, isSigned> converter, [[maybe_unused]] size_t channelIndex) {
155 static_assert(std::is_same_v<UserType, CookedType>);
156 if constexpr(!isRaw) {
157 auto* begin = reinterpret_cast<RawType*>(_rawAccessor->begin(_registerInfo.address));
158 for(auto [itsrc, itdst] = std::make_pair(begin, buffer_2D[0].begin()); itdst != buffer_2D[0].end();
159 ++itsrc, ++itdst) {
160 *itdst = converter.toCooked(*itsrc);
161 }
162 }
163 else {
164 // This function will never be called for raw accessors
165 assert(false);
166 }
167 }
168
169 /********************************************************************************************************************/
170
171 template<typename UserType, bool isRaw>
173 TransferType type, VersionNumber versionNumber) {
174 if(!_dev->isOpen()) throw ChimeraTK::logic_error("Device not opened.");
175 // raw accessor preWrite must be called before our _prePostActionsImplementor.doPreWrite(), as it needs to
176 // prepare the buffer in case of unaligned access and acquire the lock.
177 _rawAccessor->preWrite(type, versionNumber);
178
179 if constexpr(!isRaw || std::is_same<UserType, std::string>::value) {
180 if(!_registerInfo.isWriteable()) {
182 "NumericAddressedBackend: Writing to a non-writeable register is not allowed (Register name: " +
183 _registerInfo.getRegisterName() + ").");
184 }
185 _converterLoopHelper->doPreWrite();
186 }
187 else {
188 // optimised variant for raw transfers (unless type is a string)
189 auto* itdst = _rawAccessor->begin(_registerInfo.address);
190 auto itsrc = buffer_2D[0].begin();
191 memcpy(&(*itdst), &(*itsrc), buffer_2D[0].size() * sizeof(UserType));
192 }
193
194 _rawAccessor->setDataValidity(this->_dataValidity);
195 }
196
197 /********************************************************************************************************************/
198
199 template<typename UserType, bool isRaw>
200 template<class CookedType, typename RawType, RawConverter::SignificantBitsCase sc, RawConverter::FractionalCase fc,
201 bool isSigned>
203 RawConverter::Converter<CookedType, RawType, sc, fc, isSigned> converter, [[maybe_unused]] size_t channelIndex) {
204 static_assert(std::is_same_v<UserType, CookedType>);
205 if constexpr(!isRaw) {
206 auto* begin = reinterpret_cast<RawType*>(_rawAccessor->begin(_registerInfo.address));
207 for(auto [itsrc, itdst] = std::make_pair(buffer_2D[0].begin(), begin); itsrc != buffer_2D[0].end();
208 ++itsrc, ++itdst) {
209 *itdst = converter.toRaw(*itsrc);
210 }
211 }
212 else {
213 // This function will never be called for raw accessors.
214 assert(false);
215 }
216 }
217
218 /********************************************************************************************************************/
219
220 template<typename UserType, bool isRaw>
222 if(!_dev->isOpen()) throw ChimeraTK::logic_error("Device not opened.");
223 if(!_registerInfo.isReadable()) {
225 "NumericAddressedBackend: Reading from a non-readable register is not allowed (Register name: " +
226 _registerInfo.getRegisterName() + ").");
227 }
228 _rawAccessor->preRead(type);
229 }
230
231 /********************************************************************************************************************/
232
233 template<typename UserType, bool isRaw>
235 TransferType type, VersionNumber versionNumber) {
236 if(!_dev->isOpen()) return; // do not delegate if exception was thrown by us in doPreWrite
237 _rawAccessor->setActiveException(this->_activeException);
238 _rawAccessor->postWrite(type, versionNumber);
239 }
240
241 /********************************************************************************************************************/
242
243 template<typename UserType, bool isRaw>
245 const boost::shared_ptr<TransferElement const>& other) const {
246 auto rhsCasted = boost::dynamic_pointer_cast<const NumericAddressedBackendRegisterAccessor<UserType, isRaw>>(other);
247 if(rhsCasted.get() == this) {
248 return false;
249 }
250 if(!rhsCasted) return false;
251 if(_dev != rhsCasted->_dev) return false;
252 if(_registerInfo != rhsCasted->_registerInfo) return false;
253 // No need to compare the RawConverters, since they are based on the registerInfo and UserType only.
254 return true;
255 }
256
257 /********************************************************************************************************************/
258
259 template<typename UserType, bool isRaw>
261 return isReadable() && !isWriteable();
262 }
263
264 /********************************************************************************************************************/
265
266 template<typename UserType, bool isRaw>
268 return _registerInfo.isReadable();
269 }
270
271 /********************************************************************************************************************/
272
273 template<typename UserType, bool isRaw>
275 return _registerInfo.isWriteable();
276 }
277
278 /********************************************************************************************************************/
279
280 template<typename UserType, bool isRaw>
282 boost::shared_ptr<DeviceBackend> exceptionBackend) {
283 this->_exceptionBackend = exceptionBackend;
284 _rawAccessor->setExceptionBackend(exceptionBackend);
285 }
286
287 /********************************************************************************************************************/
288
289 template<typename UserType, bool isRaw>
290 std::vector<boost::shared_ptr<TransferElement>> NumericAddressedBackendRegisterAccessor<UserType,
291 isRaw>::getHardwareAccessingElements() {
292 return _rawAccessor->getHardwareAccessingElements();
293 }
294
295 /********************************************************************************************************************/
296
297 template<typename UserType, bool isRaw>
298 std::list<boost::shared_ptr<TransferElement>> NumericAddressedBackendRegisterAccessor<UserType,
299 isRaw>::getInternalElements() {
300 return {_rawAccessor}; // the rawAccessor always returns an empty list
301 }
302
303 /********************************************************************************************************************/
304
305 template<typename UserType, bool isRaw>
307 boost::shared_ptr<TransferElement> newElement) {
308 auto casted = boost::dynamic_pointer_cast<NumericAddressedLowLevelTransferElement>(newElement);
309 if(casted && casted->isMergeable(_rawAccessor)) {
310 size_t newStartAddress = std::min(casted->_startAddress, _rawAccessor->_startAddress);
311 size_t newStopAddress = std::max(
312 casted->_startAddress + casted->_numberOfBytes, _rawAccessor->_startAddress + _rawAccessor->_numberOfBytes);
313 size_t newNumberOfBytes = newStopAddress - newStartAddress;
314 casted->changeAddress(newStartAddress, newNumberOfBytes);
315 _rawAccessor = casted;
316 }
317 _rawAccessor->setExceptionBackend(this->_exceptionBackend);
318 }
319
320 /********************************************************************************************************************/
321
322 template<typename UserType, bool isRaw>
323 template<typename COOKED_TYPE>
325 unsigned int channel, unsigned int sample) {
326 if constexpr(isRaw && std::is_integral_v<UserType>) {
327 if constexpr(isRawType<std::make_unsigned_t<UserType>>) {
328 // Note: the "UserType" is our RawType here!
329 COOKED_TYPE rv;
330 RawConverter::withConverter<COOKED_TYPE, std::make_unsigned_t<UserType>>(_registerInfo, 0, [&](auto converter) {
331 rv = converter.toCooked(
332 std::make_unsigned_t<UserType>(NDRegisterAccessor<UserType>::buffer_2D[channel][sample]));
333 });
334 return rv;
335 }
336 }
337 throw ChimeraTK::logic_error("Getting as cooked is only available for raw accessors!");
338 }
339
340 /********************************************************************************************************************/
341
342 template<typename UserType, bool isRaw>
343 template<typename COOKED_TYPE>
345 unsigned int channel, unsigned int sample, COOKED_TYPE value) {
346 if constexpr(isRaw && std::is_integral_v<UserType>) {
347 if constexpr(isRawType<std::make_unsigned_t<UserType>>) {
348 // Note: the "UserType" is our RawType here!
349 RawConverter::withConverter<COOKED_TYPE, std::make_unsigned_t<UserType>>(_registerInfo, 0, [&](auto converter) {
350 NDRegisterAccessor<UserType>::buffer_2D[channel][sample] = UserType(converter.toRaw(value));
351 });
352 }
353 return;
354 }
355 throw ChimeraTK::logic_error("Setting as cooked is only available for raw accessors!");
356 }
357
358 /********************************************************************************************************************/
359
362
363 /********************************************************************************************************************/
364
365} /* 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.
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.
void doPreWriteImpl(RawConverter::Converter< CookedType, RawType, sc, fc, isSigned > converter, size_t channelIndex)
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 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...
void doPostReadImpl(RawConverter::Converter< CookedType, RawType, sc, fc, isSigned > converter, size_t channelIndex)
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.
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
@ 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)