ChimeraTK-DeviceAccess  03.18.00
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 
5 #include "createDataConverter.h"
6 #include "Exception.h"
7 #include "NDRegisterAccessor.h"
10 
11 #include <boost/shared_ptr.hpp>
12 
13 #include <sstream>
14 
15 namespace 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) return false;
95  if(_ioDevice != rhsCasted->_ioDevice) return false;
96  if(_registerInfo != rhsCasted->_registerInfo) return false;
97  if(_converters != rhsCasted->_converters) return false;
98  return true;
99  }
100 
101  [[nodiscard]] bool isReadOnly() const override { return isReadable() && !isWriteable(); }
102 
103  [[nodiscard]] bool isReadable() const override { return _registerInfo.isReadable(); }
104 
105  [[nodiscard]] bool isWriteable() const override { return _registerInfo.isWriteable(); }
106 
107  protected:
109  std::vector<ConverterType> _converters;
110 
112  boost::shared_ptr<NumericAddressedBackend> _ioDevice;
113 
114  std::vector<int32_t> _ioBuffer;
115 
117 
118  std::vector<detail::pitched_iterator<int32_t>> _startIterators;
119  std::vector<detail::pitched_iterator<int32_t>> _endIterators;
120 
121  std::vector<boost::shared_ptr<TransferElement>> getHardwareAccessingElements() override {
122  return {boost::enable_shared_from_this<TransferElement>::shared_from_this()};
123  }
124 
125  std::list<boost::shared_ptr<TransferElement>> getInternalElements() override { return {}; }
126 
127  void replaceTransferElement(boost::shared_ptr<TransferElement> /*newElement*/) override {} // LCOV_EXCL_LINE
128 
131  };
132 
133  /********************************************************************************************************************/
134 
135  template<class UserType, class ConverterType>
137  const RegisterPath& registerPathName, size_t numberOfElements, size_t elementsOffset,
138  const boost::shared_ptr<DeviceBackend>& _backend)
139  : NDRegisterAccessor<UserType>(registerPathName, {}),
140  _ioDevice(boost::dynamic_pointer_cast<NumericAddressedBackend>(_backend)) {
141  // Obtain information about the area
142  _registerInfo = _ioDevice->_registerMap.getBackendRegister(registerPathName);
143  assert(!_registerInfo.channels.empty());
144 
145  // Create a fixed point converter for each channel
146  for(size_t i = 0; i < _registerInfo.getNumberOfChannels(); ++i) {
147  if(_registerInfo.channels[i].bitOffset % 8 != 0) {
148  throw ChimeraTK::logic_error("NumericAddressedBackendMuxedRegisterAccessor: elements must be byte aligned.");
149  }
150  _converters.emplace_back(detail::createDataConverter<ConverterType>(_registerInfo, i));
151  }
152  // check information
153  if(_registerInfo.elementPitchBits % 8 != 0) {
154  throw ChimeraTK::logic_error("NumericAddressedBackendMuxedRegisterAccessor: blocks must be byte aligned.");
155  }
156 
157  // compute effective numberOfElements
158  if(numberOfElements == 0) {
159  numberOfElements = _registerInfo.nElements;
160  }
161 
162  // check number of words
163  if(numberOfElements + elementsOffset > _registerInfo.nElements) {
164  throw ChimeraTK::logic_error("Requested number of elements (" + std::to_string(numberOfElements) + " + " +
165  std::to_string(elementsOffset) + ") exceeds the size (" + std::to_string(_registerInfo.nElements) +
166  ") of the register '" + registerPathName + "'!");
167 
168  throw ChimeraTK::logic_error("Requested number of elements exceeds the size of the register! Requested end: " +
169  std::to_string(numberOfElements + elementsOffset) +
170  ", register length: " + std::to_string(_registerInfo.nElements));
171  }
172 
173  // update register info
174  _registerInfo.nElements = numberOfElements;
175  assert(_registerInfo.elementPitchBits % 8 == 0);
176  _registerInfo.address += elementsOffset * _registerInfo.elementPitchBits / 8;
177 
178  // allocate the buffer for the converted data
179  NDRegisterAccessor<UserType>::buffer_2D.resize(_converters.size());
180  for(size_t i = 0; i < _converters.size(); ++i) {
181  NDRegisterAccessor<UserType>::buffer_2D[i].resize(_registerInfo.nElements);
182  }
183 
184  // allocate the raw io buffer. Make it one element larger to make sure we can access the last byte via int32_t*
185  _ioBuffer.resize(
186  static_cast<size_t>(_registerInfo.elementPitchBits) / 8 * _registerInfo.nElements / sizeof(int32_t) + 1);
187 
188  // compute pitched iterators for accessing the channels
189  // Silence the linter: Yes, we are doing a reinterpet cast. There is nothing we can do about it when we're
190  // bit-fiddling
191 
192  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
193  auto* ioBuffer = reinterpret_cast<uint8_t*>(&_ioBuffer[0]);
194  for(auto& c : _registerInfo.channels) {
195  assert(c.bitOffset % 8 == 0);
196  _startIterators.emplace_back(ioBuffer + c.bitOffset / 8, _registerInfo.elementPitchBits / 8);
197  _endIterators.push_back(_startIterators.back() + _registerInfo.nElements);
198  }
199  }
200 
201  /********************************************************************************************************************/
202 
203  template<class UserType, class ConverterType>
205  assert(_registerInfo.elementPitchBits % 8 == 0);
206 
207  auto nbt = _registerInfo.elementPitchBits / 8 * _registerInfo.nElements;
208  nbt = ((nbt - 1) / 4 + 1) * 4; // round up to multiple of 4 bytes
209 
210  _ioDevice->read(_registerInfo.bar, _registerInfo.address, _ioBuffer.data(), nbt);
211  }
212 
213  /********************************************************************************************************************/
214 
215  template<class UserType, class ConverterType>
217  TransferType, bool hasNewData) {
218  if(hasNewData) {
219  for(size_t i = 0; i < _converters.size(); ++i) {
220  _converters[i].template vectorToCooked<UserType>(_startIterators[i], _endIterators[i], buffer_2D[i].begin());
221  }
222  // it is acceptable to create the version number in post read because this accessor does not have
223  // wait_for_new_data. It is basically synchronous.
224  this->_versionNumber = {};
225 
226  // we just read good data. Set validity back to ok if someone marked it faulty for writing.
227  this->_dataValidity = DataValidity::ok;
228  }
229  }
230 
231  /********************************************************************************************************************/
232 
233  template<class UserType, class ConverterType>
235  assert(_registerInfo.elementPitchBits % 8 == 0);
236 
237  auto nbt = _registerInfo.elementPitchBits / 8 * _registerInfo.nElements;
238  nbt = ((nbt - 1) / 4 + 1) * 4; // round up to multiple of 4 bytes
239 
240  _ioDevice->write(_registerInfo.bar, _registerInfo.address, &(_ioBuffer[0]), nbt);
241  return false;
242  }
243 
244  /********************************************************************************************************************/
245 
246  template<class UserType, class ConverterType>
248  if(!_ioDevice->isOpen()) throw ChimeraTK::logic_error("Device not opened.");
249 
250  assert(_registerInfo.channels.size() == _converters.size());
251 
252  // Convert channel by channel
253  for(size_t i = 0; i < _registerInfo.channels.size(); ++i) {
254  callForRawType(_registerInfo.channels[i].getRawType(), [&](auto x) {
255  using RawType = decltype(x);
256 
257  // Call FixedPointConverter::toRaw() for each value in the channel. The result (C++ type int32) is written to
258  // the target buffer through the pitched iterators after converting it into the RawType matching the actual
259  // bit width of the channel. This is important to avoid overwriting data of other channels.
260  std::transform(buffer_2D[i].begin(), buffer_2D[i].end(), detail::pitched_iterator<RawType>(_startIterators[i]),
261  [&](UserType cookedValue) { return _converters[i].toRaw(cookedValue); });
262  });
263  }
264  }
265 
266  /********************************************************************************************************************/
267 
268  DECLARE_MULTI_TEMPLATE_FOR_CHIMERATK_USER_TYPES(NumericAddressedBackendMuxedRegisterAccessor, FixedPointConverter);
270  NumericAddressedBackendMuxedRegisterAccessor, IEEE754_SingleConverter);
271 
272 } // namespace ChimeraTK
ChimeraTK::NumericAddressedBackendMuxedRegisterAccessor::mayReplaceOther
bool mayReplaceOther(const boost::shared_ptr< TransferElement const > &other) const override
Definition: NumericAddressedBackendMuxedRegisterAccessor.h:90
ChimeraTK::NumericAddressedBackendMuxedRegisterAccessor::getInternalElements
std::list< boost::shared_ptr< TransferElement > > getInternalElements() override
Definition: NumericAddressedBackendMuxedRegisterAccessor.h:125
ChimeraTK::NumericAddressedBackendMuxedRegisterAccessor::isReadable
bool isReadable() const override
Definition: NumericAddressedBackendMuxedRegisterAccessor.h:103
ChimeraTK::NumericAddressedRegisterInfo
Definition: NumericAddressedRegisterCatalogue.h:15
ChimeraTK::NumericAddressedBackendMuxedRegisterAccessor::isReadOnly
bool isReadOnly() const override
Definition: NumericAddressedBackendMuxedRegisterAccessor.h:101
ChimeraTK::NumericAddressedBackendMuxedRegisterAccessor::replaceTransferElement
void replaceTransferElement(boost::shared_ptr< TransferElement >) override
Definition: NumericAddressedBackendMuxedRegisterAccessor.h:127
ChimeraTK::NDRegisterAccessor::buffer_2D
std::vector< std::vector< UserType > > buffer_2D
Buffer of converted data elements.
Definition: NDRegisterAccessor.h:123
ChimeraTK::operator*
RegisterPath operator*(const RegisterPath &leftHandSide, int rightHandSide)
Definition: RegisterPath.cc:47
ChimeraTK::operator+
std::string operator+(const std::string &leftHandSide, const RegisterPath &rightHandSide)
non-member + operator for RegisterPath: concatenate with normal strings.
Definition: RegisterPath.cc:26
ChimeraTK::NumericAddressedBackendMuxedRegisterAccessor::NumericAddressedBackendMuxedRegisterAccessor
NumericAddressedBackendMuxedRegisterAccessor(const RegisterPath &registerPathName, size_t numberOfElements, size_t elementsOffset, const boost::shared_ptr< DeviceBackend > &_backend)
Definition: NumericAddressedBackendMuxedRegisterAccessor.h:136
ChimeraTK::NumericAddressedBackendMuxedRegisterAccessor::_ioBuffer
std::vector< int32_t > _ioBuffer
Definition: NumericAddressedBackendMuxedRegisterAccessor.h:114
ChimeraTK::NumericAddressedBackendMuxedRegisterAccessor::doReadTransferSynchronously
void doReadTransferSynchronously() override
Definition: NumericAddressedBackendMuxedRegisterAccessor.h:204
ChimeraTK::NumericAddressedBackendMuxedRegisterAccessor::doPreWrite
void doPreWrite(TransferType type, VersionNumber versionNumber) override
Definition: NumericAddressedBackendMuxedRegisterAccessor.h:247
ChimeraTK::NumericAddressedBackendMuxedRegisterAccessor::doPreRead
void doPreRead(TransferType) override
Definition: NumericAddressedBackendMuxedRegisterAccessor.h:86
ChimeraTK::NumericAddressedBackendMuxedRegisterAccessor::doWriteTransfer
bool doWriteTransfer(ChimeraTK::VersionNumber versionNumber) override
Definition: NumericAddressedBackendMuxedRegisterAccessor.h:234
NumericAddressedBackend.h
ChimeraTK::DataValidity::ok
@ ok
NDRegisterAccessor.h
ChimeraTK::NumericAddressedBackendMuxedRegisterAccessor::_registerInfo
NumericAddressedRegisterInfo _registerInfo
Definition: NumericAddressedBackendMuxedRegisterAccessor.h:116
ChimeraTK::callForRawType
void callForRawType(const DataType &type, LAMBDATYPE lambda)
callForRawType() is similar to callForType(), just with a subset of supported data types which can be...
Definition: SupportedUserTypes.h:1024
ChimeraTK::NumericAddressedBackendMuxedRegisterAccessor::doPostRead
void doPostRead(TransferType type, bool hasNewData) override
Definition: NumericAddressedBackendMuxedRegisterAccessor.h:216
ChimeraTK::TransferElement::_exceptionBackend
boost::shared_ptr< DeviceBackend > _exceptionBackend
The backend to which the runtime_errors are reported via DeviceBackend::setException().
Definition: TransferElement.h:313
ChimeraTK::MULTIPLEXED_SEQUENCE_PREFIX
constexpr auto MULTIPLEXED_SEQUENCE_PREFIX
Definition: NumericAddressedBackendMuxedRegisterAccessor.h:17
ChimeraTK::TransferType
TransferType
Used to indicate the applicable operation on a Transferelement.
Definition: TransferElement.h:51
ChimeraTK::NumericAddressedBackendMuxedRegisterAccessor::_ioDevice
boost::shared_ptr< NumericAddressedBackend > _ioDevice
The device from (/to) which to perform the DMA transfer.
Definition: NumericAddressedBackendMuxedRegisterAccessor.h:112
createDataConverter.h
ChimeraTK::SEQUENCE_PREFIX
constexpr auto SEQUENCE_PREFIX
Definition: NumericAddressedBackendMuxedRegisterAccessor.h:18
NumericAddressedRegisterCatalogue.h
DECLARE_MULTI_TEMPLATE_FOR_CHIMERATK_USER_TYPES
#define DECLARE_MULTI_TEMPLATE_FOR_CHIMERATK_USER_TYPES(TemplateClass,...)
Macro to declare a template class with multiple template parameters for all supported user types.
Definition: SupportedUserTypes.h:532
ChimeraTK::NumericAddressedRegisterInfo::isWriteable
bool isWriteable() const override
Return whether the register is writeable.
Definition: NumericAddressedRegisterCatalogue.h:90
ChimeraTK::NumericAddressedBackendMuxedRegisterAccessor::isWriteable
bool isWriteable() const override
Definition: NumericAddressedBackendMuxedRegisterAccessor.h:105
ChimeraTK::RegisterPath
Class to store a register path name.
Definition: RegisterPath.h:16
ChimeraTK::NumericAddressedBackendMuxedRegisterAccessor::getHardwareAccessingElements
std::vector< boost::shared_ptr< TransferElement > > getHardwareAccessingElements() override
Definition: NumericAddressedBackendMuxedRegisterAccessor.h:121
ChimeraTK::NumericAddressedBackendMuxedRegisterAccessor::_endIterators
std::vector< detail::pitched_iterator< int32_t > > _endIterators
Definition: NumericAddressedBackendMuxedRegisterAccessor.h:119
ChimeraTK::VersionNumber
Class for generating and holding version numbers without exposing a numeric representation.
Definition: VersionNumber.h:23
ChimeraTK::NumericAddressedBackendMuxedRegisterAccessor::_startIterators
std::vector< detail::pitched_iterator< int32_t > > _startIterators
Definition: NumericAddressedBackendMuxedRegisterAccessor.h:118
Exception.h
ChimeraTK::NumericAddressedRegisterInfo::isReadable
bool isReadable() const override
Return whether the register is readable.
Definition: NumericAddressedRegisterCatalogue.h:85
ChimeraTK
Definition: DummyBackend.h:16
ChimeraTK::to_string
std::string to_string(Boolean &value)
Definition: SupportedUserTypes.h:59
ChimeraTK::NumericAddressedBackendMuxedRegisterAccessor::_converters
std::vector< ConverterType > _converters
One converter for each sequence.
Definition: NumericAddressedBackendMuxedRegisterAccessor.h:109
ChimeraTK::NDRegisterAccessor
N-dimensional register accessor.
Definition: ForwardDeclarations.h:17
ChimeraTK::logic_error
Exception thrown when a logic error has occured.
Definition: Exception.h:51
ChimeraTK::MEM_MULTIPLEXED_PREFIX
constexpr auto MEM_MULTIPLEXED_PREFIX
Definition: NumericAddressedBackendMuxedRegisterAccessor.h:20
ChimeraTK::NumericAddressedBackendMuxedRegisterAccessor
Implementation of the NDRegisterAccessor for NumericAddressedBackends for multiplexd 2D registers.
Definition: NumericAddressedBackendMuxedRegisterAccessor.h:73