ChimeraTK-DeviceAccess 03.25.00
Loading...
Searching...
No Matches
NumericAddressedBackendMuxedRegisterAccessor.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
6#include "Exception.h"
10
11#include <boost/shared_ptr.hpp>
12
13#include <sstream>
14
15namespace ChimeraTK {
16
17 constexpr auto MULTIPLEXED_SEQUENCE_PREFIX = "AREA_MULTIPLEXED_SEQUENCE_";
18 constexpr auto SEQUENCE_PREFIX = "SEQUENCE_";
19
20 constexpr auto MEM_MULTIPLEXED_PREFIX = "MEM_MULTIPLEXED_";
21
22 /********************************************************************************************************************/
23
24 namespace detail {
25
27 template<typename DATA_TYPE>
28 struct pitched_iterator {
29 // standard iterator traits
30 using iterator_category = std::random_access_iterator_tag;
31 using value_type = DATA_TYPE;
32 using difference_type = std::ptrdiff_t;
33 using pointer = DATA_TYPE*;
34 using reference = DATA_TYPE&;
35
36 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
37 pitched_iterator(void* begin, size_t pitch) : _ptr(reinterpret_cast<std::byte*>(begin)), _pitch(pitch) {}
38
39 template<typename OTHER_DATA_TYPE>
40 explicit pitched_iterator(pitched_iterator<OTHER_DATA_TYPE>& other) : _ptr(other._ptr), _pitch(other._pitch) {}
41
42 pitched_iterator& operator++() {
43 _ptr += _pitch;
44 return *this;
45 }
46 pitched_iterator operator++(int) {
47 pitched_iterator retval = *this;
48 ++(*this);
49 return retval;
50 }
51 pitched_iterator operator+(size_t n) { return pitched_iterator(_ptr + n * _pitch, _pitch); }
52 bool operator==(pitched_iterator other) const { return _ptr == other._ptr; }
53 bool operator!=(pitched_iterator other) const { return !(*this == other); }
54 size_t operator-(pitched_iterator other) const { return _ptr - other._ptr; }
55 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
56 DATA_TYPE& operator*() const { return *reinterpret_cast<DATA_TYPE*>(_ptr); }
57
58 private:
59 std::byte* _ptr;
60 const size_t _pitch;
61
62 template<typename OTHER_DATA_TYPE>
63 friend struct pitched_iterator;
64 };
65
66 } // namespace detail
67
68 /********************************************************************************************************************/
72 template<class UserType, class ConverterType>
74 public:
75 NumericAddressedBackendMuxedRegisterAccessor(const RegisterPath& registerPathName, size_t numberOfElements,
76 size_t elementsOffset, const boost::shared_ptr<DeviceBackend>& _backend);
77
78 void doReadTransferSynchronously() override;
79
80 void doPostRead(TransferType type, bool hasNewData) override;
81
82 bool doWriteTransfer(ChimeraTK::VersionNumber versionNumber) override;
83
84 void doPreWrite(TransferType type, VersionNumber versionNumber) override;
85
86 void doPreRead(TransferType) override {
87 if(!_ioDevice->isOpen()) throw ChimeraTK::logic_error("Device not opened.");
88 }
89
90 [[nodiscard]] bool mayReplaceOther(const boost::shared_ptr<TransferElement const>& other) const override {
91 auto rhsCasted =
92 boost::dynamic_pointer_cast<const NumericAddressedBackendMuxedRegisterAccessor<UserType, ConverterType>>(
93 other);
94 if(rhsCasted.get() == this) {
95 return false;
96 }
97 if(!rhsCasted) return false;
98 if(_ioDevice != rhsCasted->_ioDevice) return false;
99 if(_registerInfo != rhsCasted->_registerInfo) return false;
100 if(_converters != rhsCasted->_converters) return false;
101 return true;
102 }
103
104 [[nodiscard]] bool isReadOnly() const override { return isReadable() && !isWriteable(); }
105
106 [[nodiscard]] bool isReadable() const override { return _registerInfo.isReadable(); }
107
108 [[nodiscard]] bool isWriteable() const override { return _registerInfo.isWriteable(); }
109
110 protected:
112 std::vector<ConverterType> _converters;
113
115 boost::shared_ptr<NumericAddressedBackend> _ioDevice;
116
117 std::vector<int32_t> _ioBuffer;
118
120
121 std::vector<detail::pitched_iterator<int32_t>> _startIterators;
122 std::vector<detail::pitched_iterator<int32_t>> _endIterators;
123
124 std::vector<boost::shared_ptr<TransferElement>> getHardwareAccessingElements() override {
125 return {boost::enable_shared_from_this<TransferElement>::shared_from_this()};
126 }
127
128 std::list<boost::shared_ptr<TransferElement>> getInternalElements() override { return {}; }
129
130 void replaceTransferElement(boost::shared_ptr<TransferElement> /*newElement*/) override {} // LCOV_EXCL_LINE
131
132 using NDRegisterAccessor<UserType>::buffer_2D;
134 };
135
136 /********************************************************************************************************************/
137
138 template<class UserType, class ConverterType>
140 const RegisterPath& registerPathName, size_t numberOfElements, size_t elementsOffset,
141 const boost::shared_ptr<DeviceBackend>& _backend)
142 : NDRegisterAccessor<UserType>(registerPathName, {}),
143 _ioDevice(boost::dynamic_pointer_cast<NumericAddressedBackend>(_backend)) {
144 // Obtain information about the area
145 _registerInfo = _ioDevice->_registerMap.getBackendRegister(registerPathName);
146 assert(!_registerInfo.channels.empty());
147
148 // Create a fixed point converter for each channel
149 for(size_t i = 0; i < _registerInfo.getNumberOfChannels(); ++i) {
150 if(_registerInfo.channels[i].bitOffset % 8 != 0) {
151 throw ChimeraTK::logic_error("NumericAddressedBackendMuxedRegisterAccessor: elements must be byte aligned.");
152 }
153 _converters.emplace_back(detail::createDataConverter<ConverterType>(_registerInfo, i));
154 }
155 // check information
156 if(_registerInfo.elementPitchBits % 8 != 0) {
157 throw ChimeraTK::logic_error("NumericAddressedBackendMuxedRegisterAccessor: blocks must be byte aligned.");
158 }
159
160 // compute effective numberOfElements
161 if(numberOfElements == 0) {
162 numberOfElements = _registerInfo.nElements;
163 }
164
165 // check number of words
166 if(numberOfElements + elementsOffset > _registerInfo.nElements) {
167 throw ChimeraTK::logic_error("Requested number of elements (" + std::to_string(numberOfElements) + " + " +
168 std::to_string(elementsOffset) + ") exceeds the size (" + std::to_string(_registerInfo.nElements) +
169 ") of the register '" + registerPathName + "'!");
170
171 throw ChimeraTK::logic_error("Requested number of elements exceeds the size of the register! Requested end: " +
172 std::to_string(numberOfElements + elementsOffset) +
173 ", register length: " + std::to_string(_registerInfo.nElements));
174 }
175
176 // update register info
177 _registerInfo.nElements = numberOfElements;
178 assert(_registerInfo.elementPitchBits % 8 == 0);
179 _registerInfo.address += elementsOffset * _registerInfo.elementPitchBits / 8;
180
181 // allocate the buffer for the converted data
182 NDRegisterAccessor<UserType>::buffer_2D.resize(_converters.size());
183 for(size_t i = 0; i < _converters.size(); ++i) {
184 NDRegisterAccessor<UserType>::buffer_2D[i].resize(_registerInfo.nElements);
185 }
186
187 // allocate the raw io buffer. Make it one element larger to make sure we can access the last byte via int32_t*
188 _ioBuffer.resize(
189 static_cast<size_t>(_registerInfo.elementPitchBits) / 8 * _registerInfo.nElements / sizeof(int32_t) + 1);
190
191 // compute pitched iterators for accessing the channels
192 // Silence the linter: Yes, we are doing a reinterpet cast. There is nothing we can do about it when we're
193 // bit-fiddling
194
195 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
196 auto* ioBuffer = reinterpret_cast<uint8_t*>(&_ioBuffer[0]);
197 for(auto& c : _registerInfo.channels) {
198 assert(c.bitOffset % 8 == 0);
199 _startIterators.emplace_back(ioBuffer + c.bitOffset / 8, _registerInfo.elementPitchBits / 8);
200 _endIterators.push_back(_startIterators.back() + _registerInfo.nElements);
201 }
202 }
203
204 /********************************************************************************************************************/
205
206 template<class UserType, class ConverterType>
208 assert(_registerInfo.elementPitchBits % 8 == 0);
209
210 auto nbt = _registerInfo.elementPitchBits / 8 * _registerInfo.nElements;
211 nbt = ((nbt - 1) / 4 + 1) * 4; // round up to multiple of 4 bytes
212
213 _ioDevice->read(_registerInfo.bar, _registerInfo.address, _ioBuffer.data(), nbt);
214 }
215
216 /********************************************************************************************************************/
217
218 template<class UserType, class ConverterType>
220 TransferType, bool hasNewData) {
221 if(hasNewData) {
222 for(size_t i = 0; i < _converters.size(); ++i) {
223 _converters[i].template vectorToCooked<UserType>(_startIterators[i], _endIterators[i], buffer_2D[i].begin());
224 }
225 // it is acceptable to create the version number in post read because this accessor does not have
226 // wait_for_new_data. It is basically synchronous.
227 this->_versionNumber = {};
228
229 // we just read good data. Set validity back to ok if someone marked it faulty for writing.
230 this->_dataValidity = DataValidity::ok;
231 }
232 }
233
234 /********************************************************************************************************************/
235
236 template<class UserType, class ConverterType>
238 assert(_registerInfo.elementPitchBits % 8 == 0);
239
240 auto nbt = _registerInfo.elementPitchBits / 8 * _registerInfo.nElements;
241 nbt = ((nbt - 1) / 4 + 1) * 4; // round up to multiple of 4 bytes
242
243 _ioDevice->write(_registerInfo.bar, _registerInfo.address, &(_ioBuffer[0]), nbt);
244 return false;
245 }
246
247 /********************************************************************************************************************/
248
249 template<class UserType, class ConverterType>
251 if(!_ioDevice->isOpen()) throw ChimeraTK::logic_error("Device not opened.");
252
253 assert(_registerInfo.channels.size() == _converters.size());
254
255 // Convert channel by channel
256 for(size_t i = 0; i < _registerInfo.channels.size(); ++i) {
257 callForRawType(_registerInfo.channels[i].getRawType(), [&](auto x) {
258 using RawType = decltype(x);
259
260 // Call FixedPointConverter::toRaw() for each value in the channel. The result (C++ type int32) is written to
261 // the target buffer through the pitched iterators after converting it into the RawType matching the actual
262 // bit width of the channel. This is important to avoid overwriting data of other channels.
263 std::transform(buffer_2D[i].begin(), buffer_2D[i].end(), detail::pitched_iterator<RawType>(_startIterators[i]),
264 [&](UserType cookedValue) { return _converters[i].toRaw(cookedValue); });
265 });
266 }
267 }
268
269 /********************************************************************************************************************/
270
275
276} // 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.
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.
Implementation of the NDRegisterAccessor for NumericAddressedBackends for multiplexd 2D registers.
std::vector< boost::shared_ptr< TransferElement > > getHardwareAccessingElements() override
Obtain the underlying TransferElements with actual hardware access.
NumericAddressedBackendMuxedRegisterAccessor(const RegisterPath &registerPathName, size_t numberOfElements, size_t elementsOffset, const boost::shared_ptr< DeviceBackend > &_backend)
void doReadTransferSynchronously() override
Implementation version of readTransfer() for synchronous reads.
void doPostRead(TransferType type, bool hasNewData) override
Backend specific implementation of postRead().
boost::shared_ptr< NumericAddressedBackend > _ioDevice
The device from (/to) which to perform the DMA transfer.
bool doWriteTransfer(ChimeraTK::VersionNumber versionNumber) override
Implementation version of writeTransfer().
std::list< boost::shared_ptr< TransferElement > > getInternalElements() override
Obtain the full list of TransferElements internally used by this TransferElement.
void doPreRead(TransferType) override
Backend specific implementation of preRead().
bool isWriteable() const override
Check if transfer element is writeable.
bool isReadable() const override
Check if transfer element is readable.
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 doPreWrite(TransferType type, VersionNumber versionNumber) override
Backend specific implementation of preWrite().
void replaceTransferElement(boost::shared_ptr< TransferElement >) override
Search for all underlying TransferElements which are considered identical (see sameRegister()) with t...
std::vector< ConverterType > _converters
One converter for each sequence.
bool isReadOnly() const override
Check if transfer element is read only, i.e.
bool isReadable() const override
Return whether the register is readable.
bool isWriteable() const override
Return whether the register is writeable.
Class to store a register path name.
boost::shared_ptr< DeviceBackend > _exceptionBackend
The backend to which the runtime_errors are reported via DeviceBackend::setException().
Class for generating and holding version numbers without exposing a numeric representation.
Exception thrown when a logic error has occured.
Definition Exception.h:51
std::string operator+(const std::string &leftHandSide, const RegisterPath &rightHandSide)
non-member + operator for RegisterPath: concatenate with normal strings.
void callForRawType(const DataType &type, LAMBDATYPE lambda)
callForRawType() is similar to callForType(), just with a subset of supported data types which can be...
TransferType
Used to indicate the applicable operation on a Transferelement.
RegisterPath operator*(const RegisterPath &leftHandSide, int rightHandSide)
std::string to_string(const std::string &v)
Needs to have the same interface as FixedPointConverter, except for the constructor.