ChimeraTK-DeviceAccess 03.27.00
Loading...
Searching...
No Matches
SubdeviceRegisterWindowAccessor.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
6#include <utility>
7
8namespace ChimeraTK {
9
10 /********************************************************************************************************************/
11
12 template<typename RegisterRawType, typename ReadWriteDataType>
14 boost::shared_ptr<SubdeviceBackend> backend, const std::string& registerPathName, size_t numberOfWords,
15 size_t wordOffsetInRegister)
16 : NDRegisterAccessor<RegisterRawType>(registerPathName, {AccessMode::raw}), _backend(std::move(backend)),
17 _numberOfWords(numberOfWords) {
18 auto& targetDevice = _backend->_targetDevice;
19 auto& parameters = _backend->_parameters;
20 if(!parameters["chipSelectRegister"].empty()) {
21 _accChipSelect.replace(targetDevice->getRegisterAccessor<uint64_t>(parameters["chipSelectRegister"], 1, 0, {}));
22 }
23 _accAddress.replace(targetDevice->getRegisterAccessor<uint64_t>(parameters["address"], 1, 0, {}));
24 if(!parameters["writeData"].empty()) {
25 _accWriteData.replace(targetDevice->getRegisterAccessor<ReadWriteDataType>(parameters["writeData"], 0, 0, {}));
26 }
27 else if(!parameters["data"].empty()) {
28 _accWriteData.replace(targetDevice->getRegisterAccessor<ReadWriteDataType>(parameters["data"], 0, 0, {}));
29 }
30
31 if(!parameters["busy"].empty()) {
32 _accBusy.replace(targetDevice->getRegisterAccessor<ChimeraTK::Boolean>(parameters["busy"], 1, 0, {}));
33 }
34 else if(!parameters["status"].empty()) {
35 _accBusy.replace(targetDevice->getRegisterAccessor<ChimeraTK::Boolean>(parameters["status"], 1, 0, {}));
36 }
37
38 if(!parameters["readData"].empty()) {
39 _accReadRequest.replace(targetDevice->getRegisterAccessor<ChimeraTK::Void>(parameters["readRequest"], 1, 0, {}));
40 _accReadData.replace(targetDevice->getRegisterAccessor<ReadWriteDataType>(parameters["readData"], 0, 0, {}));
41 }
42
43 auto info = _backend->_registerMap.getBackendRegister(registerPathName);
44 _startAddress = info.address + info.elementPitchBits / 8 * wordOffsetInRegister;
45
46 assert(_backend->_type == SubdeviceBackend::Type::registerWindow);
47
48 if(_accReadData.isInitialised() && _accWriteData.isInitialised() &&
49 _accReadData.getNElements() != _accWriteData.getNElements()) {
51 "SubDeviceBackend: In RegisterWindow mode, read and write data register must have the same size!");
52 }
53
54 NDRegisterAccessor<RegisterRawType>::buffer_2D.resize(1);
55 NDRegisterAccessor<RegisterRawType>::buffer_2D[0].resize(numberOfWords);
56 _buffer.resize(numberOfWords);
57
58 _endAddress = _startAddress + _numberOfWords * sizeof(RegisterRawType);
59 if(_accWriteData.isInitialised()) {
60 _transferSize = sizeof(ReadWriteDataType) * _accWriteData.getNElements();
61 }
62 else {
63 assert(_accReadData.isInitialised());
64 _transferSize = sizeof(ReadWriteDataType) * _accReadData.getNElements();
65 }
66
67 // Fixme: find better names
68 // TransferAddresses are in units of the target read/write data register size, not in bytes.
69 _starTransferAddress = _startAddress / _transferSize; // intentionally rounded down
70 // endTransferAddress is endAddress / transferSize rounded up
71 _endTransferAddress = (_endAddress + _transferSize - 1) / _transferSize;
72
73 _zeros.assign(_transferSize, std::byte{0}); // bytes with 0 to copy from
74 }
75
76 /********************************************************************************************************************/
77
78 template<typename RegisterRawType, typename WriteDataType>
82
83 /********************************************************************************************************************/
84
85 template<typename RegisterRawType, typename WriteDataType>
87 transferImpl(TransferDirection::write);
88 return false;
89 }
90
91 /********************************************************************************************************************/
92 template<typename RegisterRawType, typename WriteDataType>
94 assert(_backend->_mutex);
95 std::lock_guard<std::mutex> lockGuard(*(_backend->_mutex));
96
97 try {
98 if(_accChipSelect.isInitialised()) {
99 _accChipSelect.setAndWrite(_backend->_chipIndex);
100 }
101
102 size_t bufferCopyOffset = 0;
103 for(size_t adr = _starTransferAddress; adr < _endTransferAddress; ++adr) {
104 // copy data between buffer and read/write data accessor
105 auto thisTransfersStartAddress = adr * _transferSize;
106 auto thisTransfersEndAddress = thisTransfersStartAddress + _transferSize;
107 auto copyStartAddress = std::max(thisTransfersStartAddress, _startAddress);
108 auto copyEndAddress = std::min(thisTransfersEndAddress, _endAddress);
109 auto copyNBytes = copyEndAddress - copyStartAddress;
110
111 // only for the first transfer we might not start copying to/from the start of the accessor buffer
112 auto deviceAccessorCopyOffset =
113 (_startAddress > thisTransfersStartAddress ? _startAddress - thisTransfersStartAddress : 0);
114
115 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
116 auto* bufferStart = reinterpret_cast<std::byte*>(_buffer.data());
117
118 // set the transfer address before starting the read/write transaction
119 _accAddress.setAndWrite(adr);
120 usleep(_backend->_addressToDataDelay); // FIXME: Do we still need this?
121
122 if(direction == TransferDirection::write) {
123 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
124 auto* accessorStart = reinterpret_cast<std::byte*>(_accWriteData.data());
125
126 // Fill leading 0s. This is intentionally not read/modify write. If there is relevant data
127 // in these bytes, the SubdeviceRegisterWindowAccessor must be aligned with the write accessor
128 // and a SubArrayDecorator or BitRangeDecorator must be used around it.
129 if(deviceAccessorCopyOffset > 0) {
130 memcpy(accessorStart, _zeros.data(), deviceAccessorCopyOffset);
131 }
132
133 memcpy(accessorStart + deviceAccessorCopyOffset, bufferStart + bufferCopyOffset, copyNBytes);
134
135 // fill trailing 0s after the copy end
136 if(copyEndAddress < thisTransfersEndAddress) {
137 memcpy(accessorStart + deviceAccessorCopyOffset + copyNBytes, _zeros.data(),
138 thisTransfersEndAddress - copyEndAddress);
139 }
140
141 _accWriteData.write();
142 }
143 else {
144 assert(direction == TransferDirection::read);
145 _accReadRequest.write();
146 }
147
148 // wait until transaction is complete
149 if(_accBusy.isInitialised()) {
150 // for 3regs/regWindow wait until status register is 0 again
151 size_t retry = 0;
152 size_t max_retry = _backend->_timeout * 1000 / _backend->_sleepTime;
153 while(true) {
154 usleep(_backend->_sleepTime);
155 if(!_accBusy.readAndGet()) {
156 break;
157 }
158 if(++retry > max_retry) {
159 throw ChimeraTK::runtime_error("Write to register '" + this->_name +
160 "' failed: timeout waiting for cleared busy flag (" + _accBusy.getName() + ")");
161 }
162 }
163 }
164 else {
165 // for 2regs, wait given time
166 usleep(_backend->_sleepTime);
167 }
168
169 if(direction == TransferDirection::read) {
170 _accReadData.read();
171 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
172 auto* accessorStart = reinterpret_cast<std::byte*>(_accReadData.data());
173 memcpy(bufferStart + bufferCopyOffset, accessorStart + deviceAccessorCopyOffset, copyNBytes);
174 }
175
176 bufferCopyOffset += copyNBytes;
177 }
178 }
179 catch(ChimeraTK::runtime_error& ex) {
180 this->_exceptionBackend->setException(ex.what());
181 throw;
182 }
183 }
184
185 /********************************************************************************************************************/
186
187 template<typename RegisterRawType, typename WriteDataType>
189 if(!_backend->isOpen()) {
190 throw ChimeraTK::logic_error("Device is not opened.");
191 }
192
193 if(!isReadable()) {
195 "SubdeviceRegisterWindowAccessor[" + this->getName() + "]: Register is not readable");
196 }
197 // only registerWindow backends are readable
198 assert(_backend->_type == SubdeviceBackend::Type::registerWindow);
199
200 // FIXME: Shouldn't these tests be done in the constructor?
201 if(_accAddress.isInitialised() && !_accAddress.isWriteable()) {
202 throw ChimeraTK::logic_error("SubdeviceRegisterWindowAccessor[" + this->getName() + "]: address register '" +
203 _accAddress.getName() + "' is not writeable.");
204 }
205 if(!_accReadData.isReadable()) {
206 throw ChimeraTK::logic_error("SubdeviceRegisterWindowAccessor[" + this->getName() + "]: read data register '" +
207 _accReadData.getName() + "' is not readable.");
208 }
209 if(_accBusy.isInitialised() && !_accBusy.isReadable()) {
210 throw ChimeraTK::logic_error("SubdeviceRegisterWindowAccessor[" + this->getName() + "]: status register '" +
211 _accBusy.getName() + "' is not readable.");
212 }
213 assert(_accReadRequest.isInitialised());
214 if(!_accReadRequest.isWriteable()) {
215 throw ChimeraTK::logic_error("SubdeviceRegisterWindowAccessor[" + this->getName() + "]: read request register '" +
216 _accReadRequest.getName() + "' is not readable.");
217 }
218
219 // Apart from the tests there is nothing to do here
220 }
221
222 /********************************************************************************************************************/
223
224 template<typename RegisterRawType, typename WriteDataType>
232
233 /********************************************************************************************************************/
234
235 template<typename RegisterRawType, typename WriteDataType>
237 if(!_backend->isOpen()) {
238 throw ChimeraTK::logic_error("Device is not opened.");
239 }
240
241 if(!_accWriteData.isInitialised()) {
243 "SubdeviceRegisterWindowAccessor[" + this->getName() + "]: Register is nor writeable");
244 }
245 // FIXME: Shouldn't these tests be done in the constructor?
246 if(_accAddress.isInitialised() && !_accAddress.isWriteable()) {
247 throw ChimeraTK::logic_error("SubdeviceRegisterWindowAccessor[" + this->getName() + "]: address register '" +
248 _accAddress.getName() + "' is not writeable.");
249 }
250 if(!_accWriteData.isWriteable()) {
251 throw ChimeraTK::logic_error("SubdeviceRegisterWindowAccessor[" + this->getName() + "]: write data register '" +
252 _accWriteData.getName() + "' is not writeable.");
253 }
254 if(_backend->needStatusParam()) {
255 if(!_accBusy.isReadable()) {
256 throw ChimeraTK::logic_error("SubdeviceRegisterWindowAccessor[" + this->getName() + "]: status register '" +
257 _accBusy.getName() + "' is not readable.");
258 }
259 }
260
261 assert(NDRegisterAccessor<RegisterRawType>::buffer_2D[0].size() == _buffer.size());
264 }
265
266 /********************************************************************************************************************/
267
268 template<typename RegisterRawType, typename WriteDataType>
272
273 /********************************************************************************************************************/
274
275 template<typename RegisterRawType, typename WriteDataType>
277 const boost::shared_ptr<TransferElement const>& other) const {
278 auto castedOther = boost::dynamic_pointer_cast<SubdeviceRegisterWindowAccessor const>(other);
279 if(!castedOther) {
280 return false;
281 }
282 if(castedOther.get() == this) {
283 return false;
284 }
285
286 return (_backend == castedOther->_backend) && (this->_name == castedOther->_name) &&
287 (_numberOfWords == castedOther->_numberOfWords) && (_startAddress == castedOther->_startAddress);
288 }
289
290 /********************************************************************************************************************/
291
292 template<typename RegisterRawType, typename WriteDataType>
294 return isReadable() && !isWriteable();
295 }
296
297 /********************************************************************************************************************/
298
299 template<typename RegisterRawType, typename WriteDataType>
301 return _accReadRequest.isInitialised();
302 }
303
304 /********************************************************************************************************************/
305
306 template<typename RegisterRawType, typename WriteDataType>
308 return _accWriteData.isInitialised();
309 }
310
311 /********************************************************************************************************************/
312
313 template<typename RegisterRawType, typename WriteDataType>
314 std::vector<boost::shared_ptr<TransferElement>> SubdeviceRegisterWindowAccessor<RegisterRawType,
315 WriteDataType>::getHardwareAccessingElements() {
316 return {boost::enable_shared_from_this<TransferElement>::shared_from_this()};
317 }
318
319 /********************************************************************************************************************/
320
321 template<typename RegisterRawType, typename WriteDataType>
322 std::list<boost::shared_ptr<TransferElement>> SubdeviceRegisterWindowAccessor<RegisterRawType,
323 WriteDataType>::getInternalElements() {
324 std::list<boost::shared_ptr<TransferElement>> retval = {_accAddress.getImpl()}; // always there
325
326 if(_accWriteData.isInitialised()) {
327 retval.emplace_back(_accWriteData.getImpl());
328 }
329 if(_accBusy.isInitialised()) { // nullprt for 2reg
330 retval.emplace_back(_accBusy.getImpl());
331 }
332 if(_accChipSelect.isInitialised()) {
333 retval.emplace_back(_accChipSelect.getImpl());
334 }
335 if(_accReadRequest.isInitialised()) {
336 retval.emplace_back(_accReadRequest.getImpl());
337 }
338 if(_accReadData.isInitialised()) {
339 retval.emplace_back(_accReadData.getImpl());
340 }
341 return retval;
342 }
343
344 /********************************************************************************************************************/
345
346 template<typename RegisterRawType, typename WriteDataType>
348 boost::shared_ptr<TransferElement>) {
349 // Nothing to replace here. The necessary read/write with the handshake cannot be merged with anything.
350 }
351
352 /********************************************************************************************************************/
353 // Code instantiations for the allowed raw types
358
359 // Compatibility stuff
364
369
374
379
380} // namespace ChimeraTK
#define INSTANTIATE_MULTI_TEMPLATE_FOR_CHIMERATK_RAW_TYPES(TemplateClass,...)
Wrapper Class to avoid vector<bool> problems.
Definition Boolean.h:16
N-dimensional register accessor.
The RegisterRawType is determined by the number of bytes in the SubDevice mapfile The ReadWriteDataTy...
void doPostRead(TransferType type, bool hasNewData) override
Backend specific implementation of postRead().
bool isReadOnly() const override
Check if transfer element is read only, i.e.
bool isWriteable() const override
Check if transfer element is writeable.
void doReadTransferSynchronously() override
Implementation version of readTransfer() for synchronous reads.
bool doWriteTransfer(ChimeraTK::VersionNumber versionNumber) override
Implementation version of writeTransfer().
void doPostWrite(TransferType type, VersionNumber) override
Backend specific implementation of postWrite().
SubdeviceRegisterWindowAccessor(boost::shared_ptr< SubdeviceBackend > backend, const std::string &registerPathName, size_t numberOfWords, size_t wordOffsetInRegister)
bool isReadable() const override
Check if transfer element is readable.
void doPreRead(TransferType type) override
Backend specific implementation of preRead().
void doPreWrite(TransferType type, VersionNumber) override
Backend specific implementation of preWrite().
void replaceTransferElement(boost::shared_ptr< TransferElement > newElement) override
Search for all underlying TransferElements which are considered identical (see sameRegister()) with t...
bool mayReplaceOther(const boost::shared_ptr< TransferElement const > &) const override
Check whether the TransferElement can be used in places where the TransferElement "other" is currentl...
void setDataValidity(DataValidity validity=DataValidity::ok)
Set the current DataValidity for this TransferElement.
DataValidity dataValidity() const
Return current validity of the data.
Class for generating and holding version numbers without exposing a numeric representation.
Wrapper Class for void.
Definition Void.h:15
Exception thrown when a logic error has occured.
Definition Exception.h:51
Exception thrown when a runtime error has occured.
Definition Exception.h:18
const char * what() const noexcept override
Return the message describing what exactly went wrong.
Definition Exception.cpp:14
@ 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.