ChimeraTK-DeviceAccess 03.25.00
Loading...
Searching...
No Matches
NumericAddressedRegisterCatalogue.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
7#include "Exception.h"
8#include "MapFileParser.h"
9#include "NumericAddress.h"
10#include "predicates.h"
11
12#include <algorithm>
13#include <cmath>
14#include <stdexcept>
15#include <utility>
16
17namespace ChimeraTK {
18
19 /********************************************************************************************************************/
20
22 uint64_t address_, uint32_t nBytes_, uint64_t bar_, uint32_t width_, int32_t nFractionalBits_, bool signedFlag_,
23 Access dataAccess_, Type dataType_, std::vector<size_t> interruptId_)
24 : pathName(pathName_), nElements(nElements_), elementPitchBits(nElements_ > 0 ? nBytes_ / nElements_ * 8 : 0),
25 bar(bar_), address(address_), registerAccess(dataAccess_), interruptId(std::move(interruptId_)),
26 channels({{0, dataType_, width_, nFractionalBits_, signedFlag_}}) {
27 assert(channels.size() == 1);
28
29 // make sure . and / is treated as similar as possible
30 pathName.setAltSeparator(".");
31
32 // consistency checks
33 if(nBytes_ > 0 && nElements_ > 0) {
34 if(nBytes_ % nElements_ != 0) {
35 // nBytes_ must be divisible by nElements_
36 throw logic_error("Number of bytes is not a multiple of number of elements for register " + pathName +
37 ". Check your map file!");
38 }
39 }
40
41 computeDataDescriptor();
42 }
43
44 /********************************************************************************************************************/
45
47 uint64_t address_, uint32_t nElements_, uint32_t elementPitchBits_, std::vector<ChannelInfo> channelInfo_,
48 Access dataAccess_, std::vector<size_t> interruptId_)
49 : pathName(pathName_), nElements(nElements_), elementPitchBits(elementPitchBits_), bar(bar_), address(address_),
50 registerAccess(dataAccess_), interruptId(std::move(interruptId_)), channels(std::move(channelInfo_)) {
51 assert(!channels.empty());
52
53 // make sure . and / is treated as similar as possible
55
57 }
58
59 /********************************************************************************************************************/
60
62 // Determine DataDescriptor. If there are multiple channels, use the "biggest" data type.
63 Type dataType = Type::VOID;
64 uint32_t width = 0;
65 int32_t nFractionalBits = 0;
66 bool signedFlag = false;
67 for(auto& c : channels) {
68 if(int(c.dataType) > int(dataType)) dataType = c.dataType;
69 if(c.width + c.nFractionalBits + c.signedFlag > width + nFractionalBits + signedFlag) {
70 width = c.width;
71 nFractionalBits = c.nFractionalBits;
72 signedFlag = c.signedFlag;
73 }
74 }
75
76 // set raw data type
77 DataType rawDataInfo{DataType::none};
78 if(channels.size() == 1) {
79 if(elementPitchBits == 0) {
80 rawDataInfo = DataType::Void;
81 }
82 else if(elementPitchBits == 8) {
83 rawDataInfo = DataType::int8;
84 }
85 else if(elementPitchBits == 16) {
86 rawDataInfo = DataType::int16;
87 }
88 else if(elementPitchBits == 32) {
89 rawDataInfo = DataType::int32;
90 }
91 else if(elementPitchBits == 64) {
92 rawDataInfo = DataType::int64;
93 }
94 else {
95 if(dataType != Type::ASCII) {
97 "Unsupported raw size: " + std::to_string(elementPitchBits) + " bits in register " + pathName);
98 }
99 }
100 }
101
102 // set "cooked" data type
103 if(dataType == Type::IEEE754) {
104 if(width == 32) {
105 // Largest possible number +- 3e38, smallest possible number 1e-45
106 // However, the actual precision is only 23+1 bit, which is < 1e9 relevant
107 // digits. Hence, we don't have to add the 3e38 and the 1e45, but just add
108 // the leading 0. comma and sign to the largest 45 digits
110 false, // isIntegral
111 true, // isSigned
112 3 + 45, // nDigits
113 45, // nFractionalDigits
114 rawDataInfo); // we have integer in the transport layer, or none if multiplexed
115 }
116 else if(width == 64) {
117 // smallest possible 5e-324, largest 2e308
119 false, // isIntegral
120 true, // isSigned
121 3 + 325, // nDigits
122 325, // nFractionalDigits
123 rawDataInfo);
124 }
125 else {
126 throw logic_error("Wrong data width for data type IEEE754 for register " + pathName + ". Check your map file!");
127 }
128 }
129 else if(dataType == Type::FIXED_POINT) {
130 if(width > 1) { // numeric type
131
132 if(nFractionalBits > 0) {
133 auto nDigits = static_cast<size_t>(
134 std::ceil(std::log10(std::pow(2, width))) + (signedFlag ? 1 : 0) + (nFractionalBits != 0 ? 1 : 0));
135 size_t nFractionalDigits = std::ceil(std::log10(std::pow(2, nFractionalBits)));
136
138 false, // isIntegral
139 signedFlag, // isSigned
140 nDigits, nFractionalDigits, rawDataInfo);
141 }
142 else {
143 auto nDigits =
144 static_cast<size_t>(std::ceil(std::log10(std::pow(2, width + nFractionalBits))) + (signedFlag ? 1 : 0));
145
147 true, // isIntegral
148 signedFlag, // isSigned
149 nDigits, 0, rawDataInfo);
150 }
151 }
152 else if(width == 1) { // boolean
154 }
155 else { // width == 0 -> nodata
157 }
158 }
159 else if(dataType == Type::ASCII) {
161 }
162 else if(dataType == Type::VOID) {
164 }
165 }
166
167 /********************************************************************************************************************/
168
170 return (address == rhs.address) && (bar == rhs.bar) && (nElements == rhs.nElements) && (channels == rhs.channels) &&
171 (pathName == rhs.pathName) && (elementPitchBits == rhs.elementPitchBits) &&
173 (interruptId == rhs.interruptId);
174 }
175
176 /********************************************************************************************************************/
177
181
182 /********************************************************************************************************************/
183
185 return interruptId;
186 }
187
188 /********************************************************************************************************************/
189
194
195 /********************************************************************************************************************/
196
198 if(width > 16) return DataType::int32;
199 if(width > 8) return DataType::int16;
200 return DataType::int8;
201 }
202
203 /********************************************************************************************************************/
204
206 return !operator==(rhs);
207 }
208
209 /********************************************************************************************************************/
210 /********************************************************************************************************************/
211
213 const RegisterPath& registerPathName) const {
214 auto path = registerPathName;
215 path.setAltSeparator(".");
216
217 if(path.startsWith(numeric_address::BAR())) {
218 // special treatment for numeric addresses
219 auto components = path.getComponents();
220 if(components.size() != 3) {
221 throw ChimeraTK::logic_error("Illegal numeric address: '" + (path) + "'");
222 }
223 auto bar = std::stoi(components[1]);
224 size_t pos = components[2].find_first_of('*');
225 auto address = std::stoi(components[2].substr(0, pos));
226 size_t nBytes;
227 if(pos != std::string::npos) {
228 nBytes = std::stoi(components[2].substr(pos + 1));
229 }
230 else {
231 nBytes = sizeof(int32_t);
232 }
233 auto nElements = nBytes / sizeof(int32_t);
234 if(nBytes == 0 || nBytes % sizeof(int32_t) != 0) {
235 throw ChimeraTK::logic_error("Illegal numeric address: '" + (path) + "'");
236 }
237 return NumericAddressedRegisterInfo(path, nElements, address, nBytes, bar);
238 }
239 if(path.startsWith("!")) {
240 auto canonicalInterrupt = _canonicalInterrupts.find(path);
241 if(canonicalInterrupt == _canonicalInterrupts.end()) {
242 throw ChimeraTK::logic_error("Illegal canonical interrupt path: '" + (path) + "'");
243 }
244 return NumericAddressedRegisterInfo(path, 0, 0, 0, 0, 0, 0, false,
246 canonicalInterrupt->second);
247 }
249 }
250
251 /********************************************************************************************************************/
252
253 [[nodiscard]] bool NumericAddressedRegisterCatalogue::hasRegister(const RegisterPath& registerPathName) const {
254 if(registerPathName.startsWith(numeric_address::BAR())) {
256 return true;
257 }
258 if(_canonicalInterrupts.find(registerPathName) != _canonicalInterrupts.end()) {
259 return true;
260 }
261 return BackendRegisterCatalogue::hasRegister(registerPathName);
262 }
263
264 /********************************************************************************************************************/
265
266 const std::set<std::vector<size_t>>& NumericAddressedRegisterCatalogue::getListOfInterrupts() const {
267 return _listOfInterrupts;
268 }
269
270 /********************************************************************************************************************/
271
274 _listOfInterrupts.insert(registerInfo.interruptId);
275 RegisterPath canonicalName = "!" + std::to_string(registerInfo.interruptId.front());
276 std::vector<size_t> canonicalID = {registerInfo.interruptId.front()};
277 _canonicalInterrupts[canonicalName] = canonicalID;
278 for(auto subId = ++registerInfo.interruptId.begin(); subId != registerInfo.interruptId.end(); ++subId) {
279 canonicalName += ":" + std::to_string(*subId);
280 canonicalID.push_back(*subId);
281 _canonicalInterrupts[canonicalName] = canonicalID;
282 }
283 }
285 }
286
287 /********************************************************************************************************************/
288
289 std::unique_ptr<BackendRegisterCatalogueBase> NumericAddressedRegisterCatalogue::clone() const {
290 std::unique_ptr<BackendRegisterCatalogueBase> c = std::make_unique<NumericAddressedRegisterCatalogue>();
291 auto* casted_c = dynamic_cast<NumericAddressedRegisterCatalogue*>(c.get());
292 fillFromThis(casted_c);
293 return c;
294 }
295
296 /********************************************************************************************************************/
297
300 target->_listOfInterrupts = _listOfInterrupts;
301 target->_canonicalInterrupts = _canonicalInterrupts;
302 target->_dataConsistencyRealms = _dataConsistencyRealms;
303 }
304
305 /********************************************************************************************************************/
306
308 const RegisterPath& registerPath, const std::string& realmName) {
309 _dataConsistencyRealms[registerPath] = realmName;
310 }
311
312 /********************************************************************************************************************/
313
314 std::shared_ptr<async::DataConsistencyRealm> NumericAddressedRegisterCatalogue::getDataConsistencyRealm(
315 const std::vector<size_t>& qualifiedAsyncDomainId) const {
316 if(qualifiedAsyncDomainId.empty()) {
317 return {};
318 }
319
320 // iterate _dataConsistencyRealms and check if the registerPath matches the qualifiedAsyncDomainId
321 for(auto const& [registerPath, realmName] : _dataConsistencyRealms) {
322 if(getBackendRegister(registerPath).getQualifiedAsyncId() == qualifiedAsyncDomainId) {
324 return store.getRealm(realmName);
325 }
326 }
327 return {};
328 }
329
330 /********************************************************************************************************************/
331
333 const std::vector<size_t>& qualifiedAsyncDomainId) const {
334 if(qualifiedAsyncDomainId.empty()) {
335 return {};
336 }
337
338 // iterate _dataConsistencyRealms and check if the registerPath matches the qualifiedAsyncDomainId
339 for(auto const& [registerPath, realmName] : _dataConsistencyRealms) {
340 if(getBackendRegister(registerPath).getQualifiedAsyncId() == qualifiedAsyncDomainId) {
341 return registerPath;
342 }
343 }
344 return {};
345 }
346
347 /********************************************************************************************************************/
348
349} // namespace ChimeraTK
void addRegister(const BackendRegisterInfo &registerInfo)
Add register information to the catalogue.
void fillFromThis(BackendRegisterCatalogue< BackendRegisterInfo > *target) const
Helper function for clone functions.
virtual BackendRegisterInfo getBackendRegister(const RegisterPath &registerPathName) const
Note: Override this function if backend has "hidden" registers which are not added to the map and hen...
bool hasRegister(const RegisterPath &registerPathName) const override
Check if register with the given path name exists.
unsigned int getNumberOfDimensions() const
Return number of dimensions of this register.
Class describing the actual payload data format of a register in an abstract manner.
A class to describe which of the supported data types is used.
@ int16
Signed 16 bit integer.
@ int32
Signed 32 bit integer.
@ none
The data type/concept does not exist, e.g. there is no raw transfer (do not confuse with Void)
@ int64
Signed 64 bit integer.
@ int8
Signed 8 bit integer.
void addDataConsistencyRealm(const RegisterPath &registerPath, const std::string &realmName)
const std::set< std::vector< size_t > > & getListOfInterrupts() const
bool hasRegister(const RegisterPath &registerPathName) const override
Check if register with the given path name exists.
void addRegister(const NumericAddressedRegisterInfo &registerInfo)
void fillFromThis(NumericAddressedRegisterCatalogue *target) const
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::set< std::vector< size_t > > _listOfInterrupts
set of interrupt IDs.
std::map< RegisterPath, std::vector< size_t > > _canonicalInterrupts
A canonical interrupt path consists of an exclamation mark, followed by a numeric interrupt and a col...
std::unique_ptr< BackendRegisterCatalogueBase > clone() const override
Create deep copy of the catalogue.
std::shared_ptr< async::DataConsistencyRealm > getDataConsistencyRealm(const std::vector< size_t > &qualifiedAsyncDomainId) const override
Return DataConsistencyRealm for the given qualified AsyncDomainId.
RegisterPath getDataConsistencyKeyRegisterPath(const std::vector< size_t > &qualifiedAsyncDomainId) const override
Return RegisterPath for the register containing the DataConsistencyKey value for the given qualified ...
std::map< RegisterPath, std::string > _dataConsistencyRealms
Map of data consistency key register paths to realm names.
uint32_t nElements
Number of elements in register.
std::vector< ChannelInfo > channels
Define per-channel information (bit interpretation etc.), 1D/scalars have exactly one entry.
Access
Enum describing the access mode of the register:
NumericAddressedRegisterInfo(RegisterPath const &pathName_={}, uint32_t nElements_=0, uint64_t address_=0, uint32_t nBytes_=0, uint64_t bar_=0, uint32_t width_=32, int32_t nFractionalBits_=0, bool signedFlag_=true, Access dataAccess_=Access::READ_WRITE, Type dataType_=Type::FIXED_POINT, std::vector< size_t > interruptId_={})
Constructor to set all data members for scalar/1D registers.
uint64_t bar
Upper part of the address (name originally from PCIe, meaning now generalised)
uint32_t elementPitchBits
Distance in bits (!) between two elements (of the same channel)
bool operator==(const ChimeraTK::NumericAddressedRegisterInfo &rhs) const
bool operator!=(const ChimeraTK::NumericAddressedRegisterInfo &rhs) const
std::vector< size_t > getQualifiedAsyncId() const override
Return the fully qualified async::SubDomain ID.
uint64_t address
Lower part of the address relative to BAR, in bytes.
Access registerAccess
Data access direction: Read, write, read and write or interrupt.
Class to store a register path name.
bool startsWith(const RegisterPath &compare) const
check if the register path starts with the given path
void setAltSeparator(const std::string &altSeparator)
set alternative separator.
static DataConsistencyRealmStore & getInstance()
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,...
STL namespace.
std::string to_string(const std::string &v)
DataType getRawType() const
Return raw type matching the given width.
uint32_t width
Number of significant bits in the register.