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