ChimeraTK-DeviceAccess  03.18.00
NumericAddressedBackend.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 "async/DomainImpl.h"
8 #include "Exception.h"
9 #include "MapFileParser.h"
10 #include "NumericAddress.h"
14 
15 namespace ChimeraTK {
16 
17  /********************************************************************************************************************/
18 
20  const std::string& mapFileName, std::unique_ptr<NumericAddressedRegisterCatalogue> registerMapPointer)
21  : _registerMapPointer(std::move(registerMapPointer)), _registerMap(*_registerMapPointer) {
22  FILL_VIRTUAL_FUNCTION_TEMPLATE_VTABLE(getRegisterAccessor_impl);
23  if(!mapFileName.empty()) {
24  MapFileParser parser;
25  std::tie(_registerMap, _metadataCatalogue) = parser.parse(mapFileName);
26  }
27  }
28 
29  /********************************************************************************************************************/
30 
32  if(!registerPathName.startsWith(numeric_address::BAR())) {
33  return _registerMap.getBackendRegister(registerPathName);
34  }
36 
37  auto components = registerPathName.getComponents();
38  if(components.size() != 3) {
39  throw ChimeraTK::logic_error("Illegal numeric address: '" + (registerPathName) + "'");
40  }
41  auto bar = std::stoi(components[1]);
42  size_t pos = components[2].find_first_of('*');
43  auto address = std::stoi(components[2].substr(0, pos));
44  size_t nBytes;
45  if(pos != std::string::npos) {
46  nBytes = std::stoi(components[2].substr(pos + 1));
47  }
48  else {
49  nBytes = sizeof(int32_t);
50  }
51  auto nElements = nBytes / sizeof(int32_t);
52  if(nBytes == 0 || nBytes % sizeof(int32_t) != 0) {
53  throw ChimeraTK::logic_error("Illegal numeric address: '" + (registerPathName) + "'");
54  }
55  return NumericAddressedRegisterInfo(registerPathName, nElements, address, nBytes, bar);
56  }
57 
58  /********************************************************************************************************************/
59 
60  /* Throw exception if called directly and not implemented by backend */
61  void NumericAddressedBackend::read([[maybe_unused]] uint8_t bar, [[maybe_unused]] uint32_t address,
62  [[maybe_unused]] int32_t* data, [[maybe_unused]] size_t sizeInBytes) {
63  throw ChimeraTK::logic_error("NumericAddressedBackend: internal error: interface read() called w/ 32bit address");
64  }
65 
66  /********************************************************************************************************************/
67 
68  void NumericAddressedBackend::write([[maybe_unused]] uint8_t bar, [[maybe_unused]] uint32_t address,
69  [[maybe_unused]] int32_t const* data, [[maybe_unused]] size_t sizeInBytes) {
70  throw ChimeraTK::logic_error("NumericAddressedBackend: internal error: interface write() called w/ 32bit address");
71  }
72 
73  /********************************************************************************************************************/
74 
75  /* Call 32-bit address implementation by default, for backends that don't implement 64-bit */
76  void NumericAddressedBackend::read(uint64_t bar, uint64_t address, int32_t* data, size_t sizeInBytes) {
77 #pragma GCC diagnostic push
78 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
79  read(static_cast<uint8_t>(bar), static_cast<uint32_t>(address), data, sizeInBytes);
80 #pragma GCC diagnostic pop
81  }
82 
83  /********************************************************************************************************************/
84 
85  void NumericAddressedBackend::write(uint64_t bar, uint64_t address, int32_t const* data, size_t sizeInBytes) {
86 #pragma GCC diagnostic push
87 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
88  write(static_cast<uint8_t>(bar), static_cast<uint32_t>(address), data, sizeInBytes);
89 #pragma GCC diagnostic pop
90  }
91 
92  /********************************************************************************************************************/
93 
94  // Default range of valid BARs
96  return bar <= 5 || bar == 13;
97  }
98 
99  /********************************************************************************************************************/
100 
101  template<typename UserType>
102  boost::shared_ptr<NDRegisterAccessor<UserType>> NumericAddressedBackend::getRegisterAccessor_impl(
103  const RegisterPath& registerPathName, size_t numberOfWords, size_t wordOffsetInRegister, AccessModeFlags flags) {
105  // get the interrupt information from the map file
106  auto registerInfo = _registerMap.getBackendRegister(registerPathName);
107  if(!registerInfo.getSupportedAccessModes().has(AccessMode::wait_for_new_data)) {
109  "Register " + registerPathName + " does not support AccessMode::wait_for_new_data.");
110  }
111 
112  return _asyncDomainsContainer.subscribe<NumericAddressedBackend, std::nullptr_t, UserType>(
113  boost::static_pointer_cast<NumericAddressedBackend>(shared_from_this()),
114  registerInfo.getQualifiedAsyncId().front(), _asyncIsActive, registerPathName, numberOfWords,
115  wordOffsetInRegister, flags);
116  }
117  return getSyncRegisterAccessor<UserType>(registerPathName, numberOfWords, wordOffsetInRegister, flags);
118  }
119 
120  /********************************************************************************************************************/
121 
122  template<typename UserType>
123  boost::shared_ptr<NDRegisterAccessor<UserType>> NumericAddressedBackend::getSyncRegisterAccessor(
124  const RegisterPath& registerPathName, size_t numberOfWords, size_t wordOffsetInRegister, AccessModeFlags flags) {
125  boost::shared_ptr<NDRegisterAccessor<UserType>> accessor;
126  // obtain register info
127  auto registerInfo = getRegisterInfo(registerPathName);
128 
129  // 1D or scalar register
130  if(registerInfo.getNumberOfDimensions() <= 1) {
131  if((registerInfo.channels.front().dataType == NumericAddressedRegisterInfo::Type::FIXED_POINT) ||
132  (registerInfo.channels.front().dataType == NumericAddressedRegisterInfo::Type::VOID)) {
133  if(flags.has(AccessMode::raw)) {
134  accessor = boost::shared_ptr<NDRegisterAccessor<UserType>>(
135  new NumericAddressedBackendRegisterAccessor<UserType, FixedPointConverter, true>(
136  shared_from_this(), registerPathName, numberOfWords, wordOffsetInRegister, flags));
137  }
138  else {
139  accessor = boost::shared_ptr<NDRegisterAccessor<UserType>>(
140  new NumericAddressedBackendRegisterAccessor<UserType, FixedPointConverter, false>(
141  shared_from_this(), registerPathName, numberOfWords, wordOffsetInRegister, flags));
142  }
143  }
144  else if(registerInfo.channels.front().dataType == NumericAddressedRegisterInfo::Type::IEEE754) {
145  if(flags.has(AccessMode::raw)) {
146  accessor = boost::shared_ptr<NDRegisterAccessor<UserType>>(
147  new NumericAddressedBackendRegisterAccessor<UserType, IEEE754_SingleConverter, true>(
148  shared_from_this(), registerPathName, numberOfWords, wordOffsetInRegister, flags));
149  }
150  else {
151  accessor = boost::shared_ptr<NDRegisterAccessor<UserType>>(
152  new NumericAddressedBackendRegisterAccessor<UserType, IEEE754_SingleConverter, false>(
153  shared_from_this(), registerPathName, numberOfWords, wordOffsetInRegister, flags));
154  }
155  }
156  else if(registerInfo.channels.front().dataType == NumericAddressedRegisterInfo::Type::ASCII) {
157  if constexpr(!std::is_same<UserType, std::string>::value) {
158  throw ChimeraTK::logic_error("NumericAddressedBackend: ASCII data must be read with std::string UserType.");
159  }
160  else {
161  accessor = boost::shared_ptr<NDRegisterAccessor<UserType>>(new NumericAddressedBackendASCIIAccessor(
162  shared_from_this(), registerPathName, numberOfWords, wordOffsetInRegister, flags));
163  }
164  }
165  else {
166  throw ChimeraTK::logic_error("NumericAddressedBackend: trying to get accessor for unsupported data type");
167  }
168  }
169  // 2D multiplexed register
170  else {
171  flags.checkForUnknownFlags({});
172  if(registerInfo.channels.front().dataType == NumericAddressedRegisterInfo::Type::IEEE754) {
173  accessor = boost::shared_ptr<NDRegisterAccessor<UserType>>(
174  new NumericAddressedBackendMuxedRegisterAccessor<UserType, IEEE754_SingleConverter>(
175  registerPathName, numberOfWords, wordOffsetInRegister, shared_from_this()));
176  }
177  else {
178  accessor = boost::shared_ptr<NDRegisterAccessor<UserType>>(
179  new NumericAddressedBackendMuxedRegisterAccessor<UserType, FixedPointConverter>(
180  registerPathName, numberOfWords, wordOffsetInRegister, shared_from_this()));
181  }
182  }
183 
184  accessor->setExceptionBackend(shared_from_this());
185  return accessor;
186  }
187 
188  /********************************************************************************************************************/
189 
191  _asyncIsActive = true;
192  // Iterating all async domains must happen under the container lock. We prepare a lambda that is executed via
193  // DomainsContainer::forEach().
194  auto activateDomain = [this](size_t key, boost::shared_ptr<async::Domain>& domain) {
195  auto domainImpl = boost::dynamic_pointer_cast<async::DomainImpl<std::nullptr_t>>(domain);
196  assert(domainImpl);
197  auto subscriptionDone = this->activateSubscription(key, domainImpl);
198  // Wait until the backends reports that the subscription is complete (typically set from inside another thread)
199  // before polling the initial values when activating the async domain. This is necessary to make sure we don't
200  // miss an update that came in after polling the initial value.
201  subscriptionDone.wait();
202  domainImpl->activate(nullptr);
203  };
204 
205  _asyncDomainsContainer.forEach(activateDomain);
206  }
207 
208  /********************************************************************************************************************/
209 
210  // The default implementation just returns a ready future.
211  std::future<void> NumericAddressedBackend::activateSubscription([[maybe_unused]] unsigned int interruptNumber,
212  [[maybe_unused]] boost::shared_ptr<async::DomainImpl<std::nullptr_t>> asyncDomain) {
213  std::promise<void> subscriptionDonePromise;
214  subscriptionDonePromise.set_value();
215  return subscriptionDonePromise.get_future();
216  }
217 
218  /********************************************************************************************************************/
219 
221  _asyncIsActive = false;
222 
223  _asyncDomainsContainer.forEach([](size_t, boost::shared_ptr<async::Domain>& domain) { domain->deactivate(); });
224 
225  closeImpl();
226  }
227 
228  /********************************************************************************************************************/
229 
232  }
233 
234  /********************************************************************************************************************/
235 
237  return _metadataCatalogue;
238  }
239 
240  /********************************************************************************************************************/
241 
243  _asyncIsActive = false;
244  }
245 
246  /********************************************************************************************************************/
247 
248 } // namespace ChimeraTK
ChimeraTK::NumericAddressedRegisterInfo::Type::ASCII
@ ASCII
ChimeraTK::NumericAddressedBackend::_metadataCatalogue
MetadataCatalogue _metadataCatalogue
metadata catalogue
Definition: NumericAddressedBackend.h:156
DomainsContainer.h
ChimeraTK::AccessMode::raw
@ raw
Raw access: disable any possible conversion from the original hardware data type into the given UserT...
ChimeraTK::NumericAddressedBackend::closeImpl
virtual void closeImpl()
All backends derrived from NumericAddressedBackend must implement closeImpl() instead of close.
Definition: NumericAddressedBackend.h:117
ChimeraTK::NumericAddressedBackend::close
void close() final
Deactivates all asynchronous accessors and calls closeImpl().
Definition: NumericAddressedBackend.cc:220
ChimeraTK::NumericAddressedRegisterCatalogue::getBackendRegister
NumericAddressedRegisterInfo getBackendRegister(const RegisterPath &registerPathName) const override
Note: Override this function if backend has "hidden" registers which are not added to the map and hen...
Definition: NumericAddressedRegisterCatalogue.cc:209
MapFileParser.h
ChimeraTK::NumericAddressedRegisterInfo
Definition: NumericAddressedRegisterCatalogue.h:15
ChimeraTK::async::DomainsContainer::forEach
void forEach(const std::function< void(size_t, boost::shared_ptr< Domain > &)> &executeMe)
Iterate all Domains under the container lock.
Definition: DomainsContainer.cc:91
ChimeraTK::MetadataCatalogue
Container for backend metadata.
Definition: MetadataCatalogue.h:17
NumericAddressedBackendASCIIAccessor.h
FILL_VIRTUAL_FUNCTION_TEMPLATE_VTABLE
#define FILL_VIRTUAL_FUNCTION_TEMPLATE_VTABLE(functionName)
Fill the vtable of a virtual function template defined with DEFINE_VIRTUAL_FUNCTION_TEMPLATE.
Definition: VirtualFunctionTemplate.h:84
ChimeraTK::NumericAddressedRegisterInfo::Type::VOID
@ VOID
ChimeraTK::NumericAddressedRegisterInfo::Type::IEEE754
@ IEEE754
ChimeraTK::MapFileParser
Provides method to parse MAP file.
Definition: MapFileParser.h:21
DomainImpl.h
ChimeraTK::RegisterCatalogue
Catalogue of register information.
Definition: RegisterCatalogue.h:20
ChimeraTK::AccessModeFlags::has
bool has(AccessMode flag) const
Check if a certain flag is in the set.
Definition: AccessMode.cc:20
ChimeraTK::DeviceBackendImpl::_asyncDomainsContainer
async::DomainsContainer _asyncDomainsContainer
Container for async::Domains to support wait_for_new_data.
Definition: DeviceBackendImpl.h:66
ChimeraTK::NumericAddressedBackend::write
virtual void write(uint64_t bar, uint64_t address, int32_t const *data, size_t sizeInBytes)
Write function to be implemented by backends.
Definition: NumericAddressedBackend.cc:85
NumericAddressedBackend.h
ChimeraTK::NumericAddressedBackend::read
virtual void read(uint64_t bar, uint64_t address, int32_t *data, size_t sizeInBytes)
Read function to be implemented by backends.
Definition: NumericAddressedBackend.cc:76
ChimeraTK::NumericAddressedBackend::activateAsyncRead
void activateAsyncRead() noexcept override
Activate asyncronous read for all transfer elements where AccessMode::wait_for_new_data is set.
Definition: NumericAddressedBackend.cc:190
ChimeraTK::NumericAddressedBackend::_registerMap
NumericAddressedRegisterCatalogue & _registerMap
Definition: NumericAddressedBackend.h:153
ChimeraTK::AccessMode::wait_for_new_data
@ wait_for_new_data
Make any read blocking until new data has arrived since the last read.
ChimeraTK::NumericAddressedRegisterCatalogue::clone
std::unique_ptr< BackendRegisterCatalogueBase > clone() const override
Create deep copy of the catalogue.
Definition: NumericAddressedRegisterCatalogue.cc:286
ChimeraTK::RegisterPath::startsWith
bool startsWith(const RegisterPath &compare) const
check if the register path starts with the given path
Definition: RegisterPath.h:141
ChimeraTK::numeric_address::BAR
RegisterPath BAR()
The numeric_address::BAR() function can be used to directly access registers by numeric addresses,...
Definition: NumericAddress.cc:7
ChimeraTK::NumericAddressedRegisterInfo::Type::FIXED_POINT
@ FIXED_POINT
ChimeraTK::NumericAddressedBackend::getRegisterCatalogue
RegisterCatalogue getRegisterCatalogue() const override
Return the register catalogue with detailed information on all registers.
Definition: NumericAddressedBackend.cc:230
NumericAddress.h
NumericAddressedBackendRegisterAccessor.h
ChimeraTK::async::DomainImpl
Definition: DomainImpl.h:15
ChimeraTK::NumericAddressedBackend::barIndexValid
virtual bool barIndexValid(uint64_t bar)
Function to be implemented by the backends.
Definition: NumericAddressedBackend.cc:95
ChimeraTK::NumericAddressedBackend::getRegisterInfo
NumericAddressedRegisterInfo getRegisterInfo(const RegisterPath &registerPathName)
getRegisterInfo returns a NumericAddressedRegisterInfo object for the given register.
Definition: NumericAddressedBackend.cc:31
ChimeraTK::NumericAddressedBackend::NumericAddressedBackend
NumericAddressedBackend(const std::string &mapFileName="", std::unique_ptr< NumericAddressedRegisterCatalogue > registerMapPointer=std::make_unique< NumericAddressedRegisterCatalogue >())
Definition: NumericAddressedBackend.cc:19
ChimeraTK::async::DomainsContainer::subscribe
boost::shared_ptr< AsyncNDRegisterAccessor< UserDataType > > subscribe(boost::shared_ptr< BackendType > backend, size_t domainId, bool activate, RegisterPath name, size_t numberOfWords, size_t wordOffsetInRegister, AccessModeFlags flags)
Get an accessor from a particular domain.
Definition: DomainsContainer.h:122
ChimeraTK::RegisterPath
Class to store a register path name.
Definition: RegisterPath.h:16
ChimeraTK::RegisterPath::getComponents
std::vector< std::string > getComponents() const
split path into components
Definition: RegisterPath.h:158
ChimeraTK::NumericAddressedBackend::getMetadataCatalogue
MetadataCatalogue getMetadataCatalogue() const override
Return the device metadata catalogue.
Definition: NumericAddressedBackend.cc:236
NumericAddressedBackendMuxedRegisterAccessor.h
ChimeraTK::NumericAddressedBackend::setExceptionImpl
void setExceptionImpl() noexcept override
Turn off the internal variable which remembers that async is active.
Definition: NumericAddressedBackend.cc:242
ChimeraTK::AccessModeFlags
Set of AccessMode flags with additional functionality for an easier handling.
Definition: AccessMode.h:48
Exception.h
ChimeraTK::NumericAddressedBackend::activateSubscription
virtual std::future< void > activateSubscription(uint32_t interruptNumber, boost::shared_ptr< async::DomainImpl< std::nullptr_t >> asyncDomain)
Activate/create the subscription for a given interrupt (for instance by starting the according interr...
Definition: NumericAddressedBackend.cc:211
ChimeraTK
Definition: DummyBackend.h:16
ChimeraTK::logic_error
Exception thrown when a logic error has occured.
Definition: Exception.h:51
ChimeraTK::MapFileParser::parse
std::pair< NumericAddressedRegisterCatalogue, MetadataCatalogue > parse(const std::string &file_name)
Performs parsing of specified MAP file.
Definition: MapFileParser.cpp:19