ChimeraTK-DeviceAccess 03.27.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, 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()) {
125 // do not delegate if exception was thrown by us in doPreWrite
126 return;
127 }
128
129 _rawAccessor->setActiveException(this->_activeException);
130 _rawAccessor->postRead(type, hasNewData);
131
132 if(!hasNewData) {
133 return;
134 }
135
136 if constexpr(!isRaw || std::is_same<UserType, std::string>::value) {
137 _converterLoopHelper->doPostRead();
138 }
139 else {
140 // optimised variant for raw transfers (unless type is a string)
141 auto* itsrc = _rawAccessor->begin(_registerInfo.address);
142 auto* itdst = buffer_2D[0].data();
143 memcpy(itdst, itsrc, buffer_2D[0].size() * sizeof(UserType));
144 }
145
146 // we don't put the setting of the version number into the PrePostActionImplementor
147 // because it does not need template specialisation, and the implementer does not
148 // know about _versionNumber. It's just easier here.
149 this->_versionNumber = _rawAccessor->getVersionNumber();
150 this->_dataValidity = _rawAccessor->dataValidity();
151 }
152
153 /********************************************************************************************************************/
154
155 template<typename UserType, bool isRaw>
156 template<class CookedType, typename RawType, RawConverter::SignificantBitsCase sc, RawConverter::FractionalCase fc,
157 bool isSigned>
159 RawConverter::Converter<CookedType, RawType, sc, fc, isSigned> converter, [[maybe_unused]] size_t implParameter) {
160 static_assert(std::is_same_v<UserType, CookedType>);
161 if constexpr(!isRaw) {
162 if constexpr(!std::is_same_v<RawType, ChimeraTK::Void>) {
163 auto* begin = _rawAccessor->begin(_registerInfo.address);
164 assert(begin != nullptr);
165 for(auto [itsrc, itdst] = std::make_pair(begin, buffer_2D[0].begin()); itdst != buffer_2D[0].end();
166 itsrc += sizeof(RawType), ++itdst) {
167 RawType temp;
168 memcpy(&temp, itsrc, sizeof(RawType));
169 *itdst = converter.toCooked(temp);
170 }
171 }
172 }
173 else {
174 // This function will never be called for raw accessors
175 assert(false);
176 }
177 }
178
179 /********************************************************************************************************************/
180
181 template<typename UserType, bool isRaw>
183 TransferType type, VersionNumber versionNumber) {
184 if(!_dev->isOpen()) {
185 throw ChimeraTK::logic_error("Device not opened.");
186 }
187 // raw accessor preWrite must be called before our _prePostActionsImplementor.doPreWrite(), as it needs to
188 // prepare the buffer in case of unaligned access and acquire the lock.
189 _rawAccessor->preWrite(type, versionNumber);
190
191 if constexpr(!isRaw || std::is_same<UserType, std::string>::value) {
192 if(!_registerInfo.isWriteable()) {
194 "NumericAddressedBackend: Writing to a non-writeable register is not allowed (Register name: " +
195 _registerInfo.getRegisterName() + ").");
196 }
197 _converterLoopHelper->doPreWrite();
198 }
199 else {
200 // optimised variant for raw transfers (unless type is a string)
201 auto* itdst = _rawAccessor->begin(_registerInfo.address);
202 auto itsrc = buffer_2D[0].begin();
203 memcpy(&(*itdst), &(*itsrc), buffer_2D[0].size() * sizeof(UserType));
204 }
205
206 _rawAccessor->setDataValidity(this->_dataValidity);
207 }
208
209 /********************************************************************************************************************/
210
211 template<typename UserType, bool isRaw>
212 template<class CookedType, typename RawType, RawConverter::SignificantBitsCase sc, RawConverter::FractionalCase fc,
213 bool isSigned>
215 RawConverter::Converter<CookedType, RawType, sc, fc, isSigned> converter, [[maybe_unused]] size_t implParameter) {
216 static_assert(std::is_same_v<UserType, CookedType>);
217 if constexpr(!isRaw) {
218 if constexpr(!std::is_same_v<RawType, ChimeraTK::Void>) {
219 auto* begin = _rawAccessor->begin(_registerInfo.address);
220 for(auto [itsrc, itdst] = std::make_pair(buffer_2D[0].begin(), begin); itsrc != buffer_2D[0].end();
221 ++itsrc, itdst += sizeof(RawType)) {
222 RawType temp = converter.toRaw(*itsrc);
223 memcpy(itdst, &temp, sizeof(RawType));
224 }
225 }
226 }
227 else {
228 // This function will never be called for raw accessors.
229 assert(false);
230 }
231 }
232
233 /********************************************************************************************************************/
234
235 template<typename UserType, bool isRaw>
237 if(!_dev->isOpen()) {
238 throw ChimeraTK::logic_error("Device not opened.");
239 }
240 if(!_registerInfo.isReadable()) {
242 "NumericAddressedBackend: Reading from a non-readable register is not allowed (Register name: " +
243 _registerInfo.getRegisterName() + ").");
244 }
245 _rawAccessor->preRead(type);
246 }
247
248 /********************************************************************************************************************/
249
250 template<typename UserType, bool isRaw>
252 TransferType type, VersionNumber versionNumber) {
253 if(!_dev->isOpen()) {
254 // do not delegate if exception was thrown by us in doPreWrite
255 return;
256 }
257 _rawAccessor->setActiveException(this->_activeException);
258 _rawAccessor->postWrite(type, versionNumber);
259 }
260
261 /********************************************************************************************************************/
262
263 template<typename UserType, bool isRaw>
265 const boost::shared_ptr<TransferElement const>& other) const {
266 auto rhsCasted = boost::dynamic_pointer_cast<const NumericAddressedBackendRegisterAccessor<UserType, isRaw>>(other);
267 if(rhsCasted.get() == this) {
268 return false;
269 }
270 if(!rhsCasted) {
271 return false;
272 }
273 if(_dev != rhsCasted->_dev) {
274 return false;
275 }
276 if(_registerInfo != rhsCasted->_registerInfo) {
277 return false;
278 }
279 // No need to compare the RawConverters, since they are based on the registerInfo and UserType only.
280 return true;
281 }
282
283 /********************************************************************************************************************/
284
285 template<typename UserType, bool isRaw>
287 return isReadable() && !isWriteable();
288 }
289
290 /********************************************************************************************************************/
291
292 template<typename UserType, bool isRaw>
294 return _registerInfo.isReadable();
295 }
296
297 /********************************************************************************************************************/
298
299 template<typename UserType, bool isRaw>
301 return _registerInfo.isWriteable();
302 }
303
304 /********************************************************************************************************************/
305
306 template<typename UserType, bool isRaw>
308 boost::shared_ptr<DeviceBackend> exceptionBackend) {
309 this->_exceptionBackend = exceptionBackend;
310 _rawAccessor->setExceptionBackend(exceptionBackend);
311 }
312
313 /********************************************************************************************************************/
314
315 template<typename UserType, bool isRaw>
316 std::vector<boost::shared_ptr<TransferElement>> NumericAddressedBackendRegisterAccessor<UserType,
317 isRaw>::getHardwareAccessingElements() {
318 return _rawAccessor->getHardwareAccessingElements();
319 }
320
321 /********************************************************************************************************************/
322
323 template<typename UserType, bool isRaw>
324 std::list<boost::shared_ptr<TransferElement>> NumericAddressedBackendRegisterAccessor<UserType,
325 isRaw>::getInternalElements() {
326 return {_rawAccessor}; // the rawAccessor always returns an empty list
327 }
328
329 /********************************************************************************************************************/
330
331 template<typename UserType, bool isRaw>
333 boost::shared_ptr<TransferElement> newElement) {
334 auto casted = boost::dynamic_pointer_cast<NumericAddressedLowLevelTransferElement>(newElement);
335 if(casted && casted->isMergeable(_rawAccessor)) {
336 size_t newStartAddress = std::min(casted->_startAddress, _rawAccessor->_startAddress);
337 size_t newStopAddress = std::max(
338 casted->_startAddress + casted->_numberOfBytes, _rawAccessor->_startAddress + _rawAccessor->_numberOfBytes);
339 size_t newNumberOfBytes = newStopAddress - newStartAddress;
340 casted->changeAddress(newStartAddress, newNumberOfBytes);
341 _rawAccessor = casted;
342 }
343 _rawAccessor->setExceptionBackend(this->_exceptionBackend);
344 }
345
346 /********************************************************************************************************************/
347
348 template<typename UserType, bool isRaw>
349 template<typename COOKED_TYPE>
351 unsigned int channel, unsigned int sample) {
352 if constexpr(isRaw && std::is_integral_v<UserType>) {
353 if constexpr(isRawType<std::make_unsigned_t<UserType>>) {
354 // Note: the "UserType" is our RawType here!
355 COOKED_TYPE rv;
356 RawConverter::withConverter<COOKED_TYPE, std::make_unsigned_t<UserType>>(_registerInfo, 0, [&](auto converter) {
357 rv = converter.toCooked(
358 std::make_unsigned_t<UserType>(NDRegisterAccessor<UserType>::buffer_2D[channel][sample]));
359 });
360 return rv;
361 }
362 }
363 throw ChimeraTK::logic_error("Getting as cooked is only available for raw accessors!");
364 }
365
366 /********************************************************************************************************************/
367
368 template<typename UserType, bool isRaw>
369 template<typename COOKED_TYPE>
371 unsigned int channel, unsigned int sample, COOKED_TYPE value) {
372 if constexpr(isRaw && std::is_integral_v<UserType>) {
373 if constexpr(isRawType<std::make_unsigned_t<UserType>>) {
374 // Note: the "UserType" is our RawType here!
375 RawConverter::withConverter<COOKED_TYPE, std::make_unsigned_t<UserType>>(_registerInfo, 0, [&](auto converter) {
376 NDRegisterAccessor<UserType>::buffer_2D[channel][sample] = UserType(converter.toRaw(value));
377 });
378 }
379 return;
380 }
381 throw ChimeraTK::logic_error("Setting as cooked is only available for raw accessors!");
382 }
383
384 /********************************************************************************************************************/
385
388
389 /********************************************************************************************************************/
390
391} /* 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 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.
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)