ChimeraTK-DeviceAccess 03.26.00
Loading...
Searching...
No Matches
DoubleBufferAccessor.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
5namespace ChimeraTK {
6
7 template<typename UserType>
10 const boost::shared_ptr<DeviceBackend>& backend, std::shared_ptr<detail::CountedRecursiveMutex> mutex,
11 const RegisterPath& registerPathName, size_t numberOfWords, size_t wordOffsetInRegister, AccessModeFlags flags)
12 : NDRegisterAccessor<UserType>(registerPathName, flags), _doubleBufferInfo(std::move(doubleBufferConfig)),
13 _backend(boost::dynamic_pointer_cast<NumericAddressedBackend>(backend)), _mutex(std::move(mutex)),
14 _transferLock(*_mutex, std::defer_lock) {
16 backend->getRegisterAccessor<uint32_t>(_doubleBufferInfo.enableRegisterPath, 1, _doubleBufferInfo.index, {});
17 _currentBufferNumberReg = backend->getRegisterAccessor<uint32_t>(
19
20 auto buf0Name = registerPathName + ".BUF0";
21 auto buf1Name = registerPathName + ".BUF1";
22
23 _buffer0 = backend->getRegisterAccessor<UserType>(buf0Name, numberOfWords, wordOffsetInRegister, flags);
24 _buffer1 = backend->getRegisterAccessor<UserType>(buf1Name, numberOfWords, wordOffsetInRegister, flags);
25 size_t nChannels = _buffer0->getNumberOfChannels();
26 // size_t nSamples = _buffer0->getNumberOfSamples();
27
28 this->buffer_2D.resize(nChannels);
29 for(size_t i = 0; i < nChannels; ++i) {
30 buffer_2D[i].resize(numberOfWords);
31 }
32
33 {
34 std::lock_guard<detail::CountedRecursiveMutex> lg(*_mutex);
35 if(_mutex->useCount() == 1) {
36 _enableDoubleBufferReg->accessChannel(0)[0] = 1;
38 }
39 }
40 }
41
42 template<typename UserType>
44 // Acquire lock for full transfer lifecycle
45 _transferLock.lock(); // blocks other threads
46 // acquire a lock in firmware (disable buffer swapping)
47 if(_mutex->useCount() == 1) {
48 _enableDoubleBufferReg->accessData(0) = 0;
49 _enableDoubleBufferReg->write();
50 }
51 // check which buffer is now in use by the firmware
52 _currentBufferNumberReg->read();
53 _currentBuffer = _currentBufferNumberReg->accessData(0);
54 // if current buffer 1, it means firmware writes now to buffer1, so use target (buffer 0), else use
55 // _secondBufferReg (buffer 1)
56 if(_currentBuffer == 1) {
57 _buffer0->preRead(type);
58 }
59 else {
60 _buffer1->preRead(type);
61 }
62 }
63
64 template<typename UserType>
66 if(_currentBuffer == 1) {
67 _buffer0->readTransfer();
68 }
69 else {
70 _buffer1->readTransfer();
71 }
72 }
73
74 template<typename UserType>
76 auto unlocker = cppext::finally([&] { _transferLock.unlock(); });
77 if(_currentBuffer == 1) {
78 _buffer0->postRead(type, hasNewData);
79 }
80 else {
81 _buffer1->postRead(type, hasNewData);
82 }
83
84 // release a lock in firmware (enable buffer swapping)
85 if(_mutex->useCount() == 1) {
86 _enableDoubleBufferReg->accessData(0) = 1;
87 _enableDoubleBufferReg->write();
88 }
89 // set version and data validity of this object
90 this->_versionNumber = {};
91 if(_currentBuffer == 1) {
92 this->_dataValidity = _buffer0->dataValidity();
93 }
94 else {
95 this->_dataValidity = _buffer1->dataValidity();
96 }
97
98 // Note: TransferElement Spec E.6.1 dictates that the version number and data validity needs to be set before this
99 // check.
100 if(!hasNewData) {
101 return;
102 }
103
104 // Swap buffer_2D if new data
105 if(hasNewData) {
106 auto& reg = (_currentBuffer == 1) ? _buffer0 : _buffer1;
107 for(size_t i = 0; i < reg->getNumberOfChannels(); ++i) {
108 buffer_2D[i].swap(reg->accessChannel(i));
109 }
110 }
111 }
112
113 template<typename UserType>
114 std::vector<boost::shared_ptr<TransferElement>> DoubleBufferAccessor<UserType>::getHardwareAccessingElements() {
115 // returning only this means the DoubleBufferAccessor will not be optimized when put into TransferGroup
116 // optimizing would break our handshake protocol, since it reorders transfers
117 return {TransferElement::shared_from_this()};
118 }
119
120 template<typename UserType>
121 bool DoubleBufferAccessor<UserType>::mayReplaceOther(const boost::shared_ptr<const TransferElement>& other) const {
122 auto otherDoubleBuffer = boost::dynamic_pointer_cast<const DoubleBufferAccessor<UserType>>(other);
123 if(!otherDoubleBuffer || otherDoubleBuffer.get() == this) {
124 return false;
125 }
126 return (_buffer0->mayReplaceOther(otherDoubleBuffer->_buffer0));
127 }
128
130
131} // namespace ChimeraTK
#define INSTANTIATE_TEMPLATE_FOR_CHIMERATK_USER_TYPES(TemplateClass)
Set of AccessMode flags with additional functionality for an easier handling.
Definition AccessMode.h:48
void doPostRead(TransferType type, bool hasNewData) override
Backend specific implementation of postRead().
DoubleBufferAccessor(NumericAddressedRegisterInfo::DoubleBufferInfo doubleBufferConfig, const boost::shared_ptr< DeviceBackend > &backend, std::shared_ptr< detail::CountedRecursiveMutex > mutex, const RegisterPath &registerPathName, size_t numberOfWords, size_t wordOffsetInRegister, AccessModeFlags flags)
boost::shared_ptr< NDRegisterAccessor< UserType > > _buffer1
boost::shared_ptr< ChimeraTK::NDRegisterAccessor< uint32_t > > _enableDoubleBufferReg
void doReadTransferSynchronously() override
Implementation version of readTransfer() for synchronous reads.
void doPreRead(TransferType type) override
Backend specific implementation of preRead().
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...
boost::shared_ptr< NDRegisterAccessor< UserType > > _buffer0
std::vector< boost::shared_ptr< TransferElement > > getHardwareAccessingElements() override
Obtain the underlying TransferElements with actual hardware access.
std::shared_ptr< detail::CountedRecursiveMutex > _mutex
NumericAddressedRegisterInfo::DoubleBufferInfo _doubleBufferInfo
boost::shared_ptr< ChimeraTK::NDRegisterAccessor< uint32_t > > _currentBufferNumberReg
N-dimensional register accessor.
std::vector< std::vector< UserType > > buffer_2D
Buffer of converted data elements.
Base class for address-based device backends (e.g.
Class to store a register path name.
TransferType
Used to indicate the applicable operation on a Transferelement.
STL namespace.