ChimeraTK-DeviceAccess 03.31.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 std::optional<DoubleBufferInfo> doubleBufferInfo_, bool isBitRange_)
25 : pathName(pathName_), nElements(nElements_), elementPitchBits(nElements_ > 0 ? nBytes_ / nElements_ * 8 : 0),
26 bar(bar_), address(address_), registerAccess(dataAccess_), interruptId(std::move(interruptId_)),
27 doubleBuffer(std::move(doubleBufferInfo_)), isBitRange(isBitRange_),
28 channels({{0, dataType_, width_, nFractionalBits_, signedFlag_,
29 nElements_ > 0 ? ChimeraTK::DataType("int" + std::to_string(elementPitchBits)) :
31 std::nullopt}}) {
32 assert(channels.size() == 1);
33
34 // make sure . and / is treated as similar as possible
35 pathName.setAltSeparator(".");
36
37 // consistency checks
38 if(nBytes_ > 0 && nElements_ > 0) {
39 if(nBytes_ % nElements_ != 0) {
40 // nBytes_ must be divisible by nElements_
41 throw logic_error("Number of bytes is not a multiple of number of elements for register " + pathName +
42 ". Check your map file!");
43 }
44 }
45
46 computeDataDescriptor();
47 }
48
49 /********************************************************************************************************************/
50
52 uint64_t address_, uint32_t nElements_, uint32_t elementPitchBits_, std::vector<ChannelInfo> channelInfo_,
53 Access dataAccess_, std::vector<size_t> interruptId_, std::optional<DoubleBufferInfo> doubleBufferInfo_)
54 : pathName(pathName_), nElements(nElements_), elementPitchBits(elementPitchBits_), bar(bar_), address(address_),
55 registerAccess(dataAccess_), interruptId(std::move(interruptId_)), doubleBuffer(std::move(doubleBufferInfo_)),
56 isBitRange(false), channels(std::move(channelInfo_)) {
57 assert(!channels.empty());
58
59 // make sure . and / is treated as similar as possible
61
63 }
64
65 /********************************************************************************************************************/
66
68 // Determine DataDescriptor. If there are multiple channels, use the "biggest" data type.
69 Type dataType = Type::VOID;
70 uint32_t width = 0;
71 int32_t nFractionalBits = 0;
72 bool signedFlag = false;
73 for(auto& c : channels) {
74 if(int(c.dataType) > int(dataType)) dataType = c.dataType;
75 if(c.width + c.nFractionalBits + c.signedFlag > width + nFractionalBits + signedFlag) {
76 width = c.width;
77 nFractionalBits = c.nFractionalBits;
78 signedFlag = c.signedFlag;
79 }
80 }
81
82 // set raw data type from the channel's stored raw type (its size defines the element size for the
83 // transport layer; for a strided channel slice this may differ from elementPitchBits, the stride)
84 DataType rawDataInfo{DataType::none};
85 if(channels.size() == 1) {
86 if(elementPitchBits == 0) {
87 rawDataInfo = DataType::Void;
88 }
89 else if(dataType != Type::ASCII && dataType != Type::VOID) {
90 rawDataInfo = channels.front().getRawType();
91 }
92 }
93
94 // set "cooked" data type
95 if(dataType == Type::IEEE754) {
96 if(width == 32) {
97 // Largest possible number +- 3e38, smallest possible number 1e-45
98 // However, the actual precision is only 23+1 bit, which is < 1e9 relevant
99 // digits. Hence, we don't have to add the 3e38 and the 1e45, but just add
100 // the leading 0. comma and sign to the largest 45 digits
102 false, // isIntegral
103 true, // isSigned
104 3 + 45, // nDigits
105 45, // nFractionalDigits
106 rawDataInfo); // we have integer in the transport layer, or none if multiplexed
107 }
108 else if(width == 64) {
109 // smallest possible 5e-324, largest 2e308
111 false, // isIntegral
112 true, // isSigned
113 3 + 325, // nDigits
114 325, // nFractionalDigits
115 rawDataInfo);
116 }
117 else {
118 throw logic_error("Wrong data width for data type IEEE754 for register " + pathName + ". Check your map file!");
119 }
120 }
121 else if(dataType == Type::FIXED_POINT) {
122 if(width > 1) { // numeric type
123
124 if(nFractionalBits > 0) {
125 auto nDigits = static_cast<size_t>(
126 std::ceil(std::log10(std::pow(2, width))) + (signedFlag ? 1 : 0) + (nFractionalBits != 0 ? 1 : 0));
127 size_t nFractionalDigits = std::ceil(std::log10(std::pow(2, nFractionalBits)));
128
130 false, // isIntegral
131 signedFlag, // isSigned
132 nDigits, nFractionalDigits, rawDataInfo);
133 }
134 else {
135 auto nDigits =
136 static_cast<size_t>(std::ceil(std::log10(std::pow(2, width + nFractionalBits))) + (signedFlag ? 1 : 0));
137
139 true, // isIntegral
140 signedFlag, // isSigned
141 nDigits, 0, rawDataInfo);
142 }
143 }
144 else if(width == 1) { // boolean
146 }
147 else { // width == 0 -> nodata
149 }
150 }
151 else if(dataType == Type::ASCII) {
153 }
154 else if(dataType == Type::VOID) {
156 }
157 }
158
159 /********************************************************************************************************************/
160
162 return (address == rhs.address) && (bar == rhs.bar) && (nElements == rhs.nElements) && (channels == rhs.channels) &&
163 (pathName == rhs.pathName) && (elementPitchBits == rhs.elementPitchBits) &&
165 (interruptId == rhs.interruptId);
166 }
167
168 /********************************************************************************************************************/
169
173
174 /********************************************************************************************************************/
175
177 return interruptId;
178 }
179
180 /********************************************************************************************************************/
181
183 return bitOffset == rhs.bitOffset && dataType == rhs.dataType && width == rhs.width &&
185 ((!selectedBy && !rhs.selectedBy) ||
186 (selectedBy && rhs.selectedBy &&
187 (selectedBy->val == rhs.selectedBy->val && selectedBy->regPath == rhs.selectedBy->regPath)));
188 }
189
190 /********************************************************************************************************************/
191
195
196 /********************************************************************************************************************/
197
199 return !operator==(rhs);
200 }
201
202 /********************************************************************************************************************/
203 /********************************************************************************************************************/
204
206 const RegisterPath& registerPathName) const {
207 auto path = registerPathName;
208 path.setAltSeparator(".");
209
210 if(path.startsWith(numeric_address::BAR())) {
211 // special treatment for numeric addresses
212 auto components = path.getComponents();
213 if(components.size() != 3) {
214 throw ChimeraTK::logic_error("Illegal numeric address: '" + (path) + "'");
215 }
216 auto bar = std::stoi(components[1]);
217 // Scan the second entry, starting with the signed/unsigned indicator and the bit width
218 // We use the fact that stoi stops at the first non-numeric character, so it will stop at a * if it comes
219 // after the u/s entry, or the other way around.
220 bool signedFlag = true;
221 size_t bitWidth = 32;
222 size_t pos = components[2].find_first_of("uU");
223 if(pos != std::string::npos) {
224 signedFlag = false;
225 bitWidth = std::stoi(components[2].substr(pos + 1));
226 }
227 else {
228 pos = components[2].find_first_of("sS");
229 if(pos != std::string::npos) {
230 signedFlag = true;
231 bitWidth = std::stoi(components[2].substr(pos + 1));
232 }
233 }
234 if(bitWidth != 8 && bitWidth != 16 && bitWidth != 32 && bitWidth != 64) {
235 throw ChimeraTK::logic_error("Illegal numeric address: '" + (path) + "'");
236 }
237 auto bytesPerElement = bitWidth / 8;
238
239 // now scan for the number of bytes
240 pos = components[2].find_first_of('*');
241 auto address = std::stoi(components[2].substr(0, pos));
242 size_t nBytes;
243 if(pos != std::string::npos) {
244 nBytes = std::stoi(components[2].substr(pos + 1));
245 }
246 else {
247 nBytes = bytesPerElement;
248 }
249
250 // optional explicit element pitch (stride) component, e.g. ..*..u64p32. When present, the element pitch in bits
251 // determines the number of elements (nBytes * 8 / pitchBits) and the register becomes strided.
252 auto pitchPos = components[2].find_last_of("pP");
253 size_t nElements;
254 if(pitchPos != std::string::npos) {
255 auto pitchBits = std::stoi(components[2].substr(pitchPos + 1));
256 if(pitchBits == 0 || pitchBits % 8 != 0 || nBytes * 8 % pitchBits != 0 || nBytes * 8 / pitchBits == 0) {
257 throw ChimeraTK::logic_error("Illegal numeric address: '" + (path) + "'");
258 }
259 nElements = nBytes * 8 / pitchBits;
260 }
261 else {
262 nElements = nBytes / bytesPerElement;
263 if(nBytes == 0 || nBytes % bytesPerElement != 0) {
264 throw ChimeraTK::logic_error("Illegal numeric address: '" + (path) + "'");
265 }
266 }
268 path, nElements, address, nBytes, bar, bitWidth, /* fracBits */ 0, signedFlag);
269 }
270 if(path.startsWith("!")) {
271 auto canonicalInterrupt = _canonicalInterrupts.find(path);
272 if(canonicalInterrupt == _canonicalInterrupts.end()) {
273 throw ChimeraTK::logic_error("Illegal canonical interrupt path: '" + (path) + "'");
274 }
275 return NumericAddressedRegisterInfo(path, 0, 0, 0, 0, 0, 0, false,
277 canonicalInterrupt->second);
278 }
280 }
281
282 /********************************************************************************************************************/
283
284 [[nodiscard]] bool NumericAddressedRegisterCatalogue::hasRegister(const RegisterPath& registerPathName) const {
285 if(registerPathName.startsWith(numeric_address::BAR())) {
287 return true;
288 }
289 if(_canonicalInterrupts.find(registerPathName) != _canonicalInterrupts.end()) {
290 return true;
291 }
292 return BackendRegisterCatalogue::hasRegister(registerPathName);
293 }
294
295 /********************************************************************************************************************/
296
297 const std::set<std::vector<size_t>>& NumericAddressedRegisterCatalogue::getListOfInterrupts() const {
298 return _listOfInterrupts;
299 }
300
301 /********************************************************************************************************************/
302
305 _listOfInterrupts.insert(registerInfo.interruptId);
306 RegisterPath canonicalName = "!" + std::to_string(registerInfo.interruptId.front());
307 std::vector<size_t> canonicalID = {registerInfo.interruptId.front()};
308 _canonicalInterrupts[canonicalName] = canonicalID;
309 for(auto subId = ++registerInfo.interruptId.begin(); subId != registerInfo.interruptId.end(); ++subId) {
310 canonicalName += ":" + std::to_string(*subId);
311 canonicalID.push_back(*subId);
312 _canonicalInterrupts[canonicalName] = canonicalID;
313 }
314 }
316 }
317
318 /********************************************************************************************************************/
319
320 std::unique_ptr<BackendRegisterCatalogueBase> NumericAddressedRegisterCatalogue::clone() const {
321 std::unique_ptr<BackendRegisterCatalogueBase> c = std::make_unique<NumericAddressedRegisterCatalogue>();
322 auto* casted_c = dynamic_cast<NumericAddressedRegisterCatalogue*>(c.get());
323 fillFromThis(casted_c);
324 return c;
325 }
326
327 /********************************************************************************************************************/
328
331 target->_listOfInterrupts = _listOfInterrupts;
332 target->_canonicalInterrupts = _canonicalInterrupts;
333 target->_dataConsistencyRealms = _dataConsistencyRealms;
334 }
335
336 /********************************************************************************************************************/
337
339 const RegisterPath& registerPath, const std::string& realmName) {
340 _dataConsistencyRealms[registerPath] = realmName;
341 }
342
343 /********************************************************************************************************************/
344
345 std::shared_ptr<async::DataConsistencyRealm> NumericAddressedRegisterCatalogue::getDataConsistencyRealm(
346 const std::vector<size_t>& qualifiedAsyncDomainId) const {
347 if(qualifiedAsyncDomainId.empty()) {
348 return {};
349 }
350
351 // iterate _dataConsistencyRealms and check if the registerPath matches the qualifiedAsyncDomainId
352 for(auto const& [registerPath, realmName] : _dataConsistencyRealms) {
353 if(getBackendRegister(registerPath).getQualifiedAsyncId() == qualifiedAsyncDomainId) {
355 return store.getRealm(realmName);
356 }
357 }
358 return {};
359 }
360
361 /********************************************************************************************************************/
362
364 const std::vector<size_t>& qualifiedAsyncDomainId) const {
365 if(qualifiedAsyncDomainId.empty()) {
366 return {};
367 }
368
369 // iterate _dataConsistencyRealms and check if the registerPath matches the qualifiedAsyncDomainId
370 for(auto const& [registerPath, realmName] : _dataConsistencyRealms) {
371 if(getBackendRegister(registerPath).getQualifiedAsyncId() == qualifiedAsyncDomainId) {
372 return registerPath;
373 }
374 }
375 return {};
376 }
377
378 /********************************************************************************************************************/
379
380} // 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.
@ none
The data type/concept does not exist, e.g. there is no raw transfer (do not confuse with Void)
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:
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.
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_={}, std::optional< DoubleBufferInfo > doubleBuffer_=std::nullopt, bool isBitRange_=false)
Constructor to set all data members for scalar/1D registers.
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,...
This header collects some conversion routines which should be useful both for ApplicationCore Python ...
STL namespace.
std::string to_string(const std::string &v)
uint32_t width
Number of significant bits in the register.