ChimeraTK-DeviceAccess 03.25.00
Loading...
Searching...
No Matches
GenericMuxedInterruptDistributor.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
4
5#include "async/SubDomain.h"
6
7#include <nlohmann/json.hpp>
8
9#include <boost/bimap.hpp>
10
11#include <sstream>
12#include <vector>
13
14namespace ChimeraTK::async {
16 inline static constexpr const int jsonDescriptorVersion = 1;
17 inline static constexpr const char* const VERSION_JSON_KEY = "version";
18 inline static constexpr const char* const OPTIONS_JSON_KEY = "options";
19 inline static constexpr const char* const PATH_JSON_KEY = "path";
20 };
21
23
24 /********************************************************************************************************************/
25
30 boost::bimap<std::string, GmidOptionCode> makeBimap(
31 std::initializer_list<typename boost::bimap<std::string, GmidOptionCode>::value_type> list) {
32 return {list.begin(), list.end()};
33 }
34
35 /********************************************************************************************************************/
36
37 static const auto OptionCodeMap = makeBimap({
38 {"SIE", SIE},
39 {"IER", IER},
40 {"MER", MER},
41 {"MIE", MIE},
42 {"GIE", GIE},
43 {"ISR", ISR},
44 {"ICR", ICR},
45 {"IAR", IAR},
46 {"IPR", IPR},
47 {"CIE", CIE},
48 // Then the unacceptable ones
49 {"IMR", IMaskR},
50 {"IModeR", IModeR}, // REMOVE?
51 {"ILR", ILR}, // REMOVE?
52 {"IVR", IVR}, // REMOVE?
53 {"IVAR", IVAR}, // REMOVE?
54 {"IVEAR", IVEAR}, // REMOVE?
55 {"INVALID_OPTION_CODE", INVALID_OPTION_CODE},
56 });
57
58 /********************************************************************************************************************/
59
63 inline uint32_t iToMask(const uint32_t ithInterrupt) {
64 return 0x1U << ithInterrupt;
65 }
66
67 /********************************************************************************************************************/
68
76 GmidOptionCode getOptionRegisterEnum(const std::string& opt) {
77 auto it = OptionCodeMap.left.find(opt);
78 if(it != OptionCodeMap.left.end()) { // If a valid code
79 return it->second; // return the corresponding enum.
80 }
81 // invalid code
83 }
84
85 /********************************************************************************************************************/
86
90 std::string getOptionRegisterStr(GmidOptionCode optCode) {
91 auto it = OptionCodeMap.right.find(optCode);
92 if(it != OptionCodeMap.right.end()) { // If a valid code
93 return it->second; // return the corresponding str.
94 }
95 // invalid code
96 return "INVALID_OPTION_CODE";
97 }
98
99 /********************************************************************************************************************/
100
105 std::string explainOptCode(GmidOptionCode optCode) {
106 // clang-format off
107 static std::map<GmidOptionCode, std::string> mapOptCode2Message= {
108 {ISR, "Interrupt Status Register"},
109 {IER, "Interrupt Enable Register"},
110 {MER, "Master Enable Register"},
111 {MIE, "Master Interrupt Enable"},
112 {GIE, "Global Interrupt Enable"},
113 {ICR, "Interrupt Clear Register"},
114 {IAR, "Interrupt Acknowledge Register"},
115 {IPR, "Interrupt Pending Register"},
116 {SIE, "Set Interrupt Enable"},
117 {CIE, "Clear Interrupt Enable"},
118 {IMaskR,
119 "Interrupt Mask Register, not to be confused with Interrupt Mode Register"},
120 {IModeR,
121 "Interrupt Mode Register, what AXI INTC v4.1 calls this 'IMR', not to be confused with Interrupt Mask "
122 "Register, which we call 'IMR'"},//REMOVE?
123 {IVR, "Interrupt Vector Register"}, //REMOVE?
124 {ILR, "Interrupt Level Register"},//REMOVE?
125 {IVAR, "Interrupt Vector Address Register"},//REMOVE?
126 {IVEAR, "Interrupt Vector Extended Address Register"},//REMOVE?
127 {INVALID_OPTION_CODE, "No such option code is known"}
128 };
129 // clang-format on
130 return getOptionRegisterStr(optCode) + " (" + mapOptCode2Message[optCode] + ")";
131 }
132
133 /********************************************************************************************************************/
134
139 std::string strSetToStr(const std::set<std::string>& strSet, char delimiter = ',') {
140 std::string result;
141 if(not strSet.empty()) {
142 for(const auto& str : strSet) {
143 result += str + delimiter;
144 }
145 result.pop_back();
146 }
147 return result;
148 }
149
150 /********************************************************************************************************************/
151
157 std::string intVecToStr(const std::vector<size_t>& intVec, char delimiter = ',') {
158 std::string result;
159 if(not intVec.empty()) {
160 for(const auto& i : intVec) {
161 result += std::to_string(i) + delimiter;
162 }
163 result.pop_back();
164 }
165 return result;
166 }
167
168 /********************************************************************************************************************/
169
173 std::string controllerIDToStr(const std::vector<size_t>& controllerID) {
174 return "[" + intVecToStr(controllerID, ',') + "]";
175 }
176
177 /********************************************************************************************************************/
178
187 std::pair<std::bitset<OPTION_CODE_COUNT>, std::string> parseAndValidateJsonDescriptionStrV0(
188 const std::vector<size_t>& controllerID, const std::string& descriptionJsonStr) {
189 /*
190 * throws ChimeraTK::logic_error if there are any problems:
191 * throws if there are any unexpected keys
192 * throws if there the json snippet is unparsable
193 * throws if there the path is not in the snippet
194 * throws if the json version is != 1
195 * throws if invalid options given.
196 * This does not validate the logic of the 'options' combination,
197 * this only validates the json snippet.
198 */
199
200 static constexpr const int defaultJsonDescriptorVersion = JdkV1::jsonDescriptorVersion;
201 static const std::vector<std::string> defaultOptionRegisterNames = {"ISR", "IER"};
202 std::string registerPath;
203 std::bitset<OPTION_CODE_COUNT> optionRegisterSettings(0);
204
205 nlohmann::json descriptionJson;
206 try { // **Parse jsonDescriptor and sanitize inputs**
207 descriptionJson = nlohmann::json::parse(descriptionJsonStr);
208 }
209 catch(const nlohmann::json::parse_error& ex) {
210 std::ostringstream oss;
211 oss << "GenericMuxedInterruptDistributor " << controllerIDToStr(controllerID)
212 << " was unable to parse map file json snippet " << descriptionJsonStr;
213 throw ChimeraTK::logic_error(oss.str());
214 }
215
216 // Check that there are no unexpected json keys, throw if there are unexpected keys.
217 for(auto& el : descriptionJson.items()) {
218 if(el.key() != JdkV1::PATH_JSON_KEY and el.key() != JdkV1::OPTIONS_JSON_KEY and
219 el.key() != JdkV1::VERSION_JSON_KEY) {
220 std::ostringstream oss;
221 oss << "Unknown JSON key '" << el.key() << "' provided to map file for GenericMuxedInterruptDistributor "
222 << controllerIDToStr(controllerID);
223 throw ChimeraTK::logic_error(oss.str());
224 }
225 }
226
227 try { // to get registerPath
228 descriptionJson[JdkV1::PATH_JSON_KEY].get_to(registerPath);
229 }
230 catch(const nlohmann::json::exception& e) {
231 std::ostringstream oss;
232 oss << "Map file json register path key '" << JdkV1::PATH_JSON_KEY
233 << "' error for GenericMuxedInterruptDistributor " << controllerIDToStr(controllerID) << ": " << e.what();
234 throw ChimeraTK::logic_error(oss.str());
235 }
236
237 try { // Get Version and check version
238 if(descriptionJson.value("version", defaultJsonDescriptorVersion) != JdkV1::jsonDescriptorVersion) {
239 // version: version of this json descriptor.
240 // if version != 1, or whatever the expected version currently is, throw; if no version, assume 1
241 std::ostringstream oss;
242 oss << "GenericMuxedInterruptDistributor " << controllerIDToStr(controllerID) << " expects a "
243 << JdkV1::VERSION_JSON_KEY << " " << JdkV1::jsonDescriptorVersion << " JSON descriptor, "
245 << descriptionJson.value(JdkV1::VERSION_JSON_KEY, defaultJsonDescriptorVersion) << " was received.";
246 throw ChimeraTK::logic_error(oss.str());
247 }
248 }
249 catch(const nlohmann::json::exception& e) {
250 std::ostringstream oss;
251 oss << "Map file json " << JdkV1::VERSION_JSON_KEY << " key error for GenericMuxedInterruptDistributor "
252 << controllerIDToStr(controllerID) << ": " << e.what();
253 throw ChimeraTK::logic_error(oss.str());
254 }
255
256 std::vector<std::string> optionRegisterNames;
257 try { // Get Options
258 optionRegisterNames = descriptionJson.value(JdkV1::OPTIONS_JSON_KEY, defaultOptionRegisterNames);
259 // Defaults options are used since options are optional
260 }
261 catch(const nlohmann::json::exception& e) {
262 std::ostringstream oss;
263 oss << "Map file json " << JdkV1::OPTIONS_JSON_KEY << " key error for GenericMuxedInterruptDistributor "
264 << controllerIDToStr(controllerID) << ": " << e.what();
265 throw ChimeraTK::logic_error(oss.str());
266 }
267
268 /*
269 * Note that the case where the json option is provided but empty is covered by
270 * turning on ISR in the constructor and turn on IER with a check below.
271 */
272
273 std::set<std::string> invalidOptionRegisterNamesEncountered;
274 for(const auto& orn : optionRegisterNames) {
275 if(GmidOptionCode ornCode = getOptionRegisterEnum(orn); ornCode != INVALID_OPTION_CODE) {
276 optionRegisterSettings.set(ornCode);
277 }
278 else {
279 invalidOptionRegisterNamesEncountered.insert(orn);
280 }
281 }
282 if(!invalidOptionRegisterNamesEncountered.empty()) { // Throw if there are unknown options
283 std::ostringstream oss;
284 oss << "Invalid register options " << strSetToStr(invalidOptionRegisterNamesEncountered)
285 << " supplied in the map file json descriptor (key = " << JdkV1::OPTIONS_JSON_KEY
286 << ") for GenericMuxedInterruptDistributor " << controllerIDToStr(controllerID);
287 throw ChimeraTK::logic_error(oss.str());
288 }
289
290 return std::make_pair(optionRegisterSettings, registerPath);
291 } // parseAndValidateJsonDescriptionStrV0
292
293 /********************************************************************************************************************/
294
300 std::bitset<OPTION_CODE_COUNT> const& optionRegisterSettings, std::vector<size_t> const& controllerID) {
301 /*
302 * This enforces the following rules:
303 * throw if ISR not set
304 * throw if neither IER nor IMaskR are set.
305 * throw if IMaskR and IER are both set.
306 * throw if SIE xor CIE is set
307 * throw if IMaskR is set as well as SIE or CIE
308 * throw if ICR and IAR are both set.
309 * throw if more than 0 of MIE, GIE, MER are set.
310 * Temporary: throw if IMaskR is set
311 */
312
313 /*----------------------------------------------------------------------------------------------------------------*/
314 // Throw if only SIE or CIE is there, but not both
315 if(optionRegisterSettings.test(SIE) != optionRegisterSettings.test(CIE)) {
316 std::ostringstream oss;
317 oss << "Invalid register " << JdkV1::OPTIONS_JSON_KEY
318 << " combination specified in map file json descriptor for GenericMuxedInterruptDistributor "
319 << controllerIDToStr(controllerID) << ": Only " << explainOptCode(SIE) << " or " << explainOptCode(CIE)
320 << " is set, but not both.";
321 throw ChimeraTK::logic_error(oss.str());
322 }
323 /*----------------------------------------------------------------------------------------------------------------*/
324 // Throw if IMaskR is set as well as SIE or CIE
325 if(optionRegisterSettings.test(IMaskR)) {
326 if(optionRegisterSettings.test(SIE)) {
327 std::ostringstream oss;
328 oss << "Invalid register " << JdkV1::OPTIONS_JSON_KEY
329 << " combination specified in map file json descriptor for GenericMuxedInterruptDistributor "
330 << controllerIDToStr(controllerID) << ": " << explainOptCode(SIE) << " and " << explainOptCode(IMaskR)
331 << " cannot not both be set.";
332 throw ChimeraTK::logic_error(oss.str());
333 }
334 if(optionRegisterSettings.test(CIE)) {
335 std::ostringstream oss;
336 oss << "Invalid register " << JdkV1::OPTIONS_JSON_KEY
337 << " combination specified in map file json descriptor for GenericMuxedInterruptDistributor "
338 << controllerIDToStr(controllerID) << ": " << explainOptCode(CIE) << " and " << explainOptCode(IMaskR)
339 << " cannot not both be set.";
340 throw ChimeraTK::logic_error(oss.str());
341 }
342 }
343 /*----------------------------------------------------------------------------------------------------------------*/
344 // Throw if both ICR and IAR are there
345 if(optionRegisterSettings.test(ICR) and optionRegisterSettings.test(IAR)) {
346 std::ostringstream oss;
347 oss << "Invalid register " << JdkV1::OPTIONS_JSON_KEY
348 << " combination specified in map file json descriptor for GenericMuxedInterruptDistributor "
349 << controllerIDToStr(controllerID) << ": " << explainOptCode(ICR) << " and " << explainOptCode(IAR)
350 << " cannot not both be set.";
351 throw ChimeraTK::logic_error(oss.str());
352 }
353 /*----------------------------------------------------------------------------------------------------------------*/
354 // Throw if both IMaskR and IER are there
355 if(optionRegisterSettings.test(IMaskR) and optionRegisterSettings.test(IER)) {
356 std::ostringstream oss;
357 oss << "Invalid register " << JdkV1::OPTIONS_JSON_KEY
358 << " combination specified in map file json descriptor for GenericMuxedInterruptDistributor "
359 << controllerIDToStr(controllerID) << ": Only " << explainOptCode(IMaskR) << " and " << explainOptCode(IER)
360 << " cannot not both be set.";
361 throw ChimeraTK::logic_error(oss.str());
362 }
363 /*----------------------------------------------------------------------------------------------------------------*/
364 // Throw if neither IER nor IMaskR is set. This should be impossible.
365 if(not(optionRegisterSettings.test(IMaskR) or optionRegisterSettings.test(IER))) {
366 std::ostringstream oss;
367 oss << "Invalid register " << JdkV1::OPTIONS_JSON_KEY << " for GenericMuxedInterruptDistributor "
368 << controllerIDToStr(controllerID) << ": Neither " << explainOptCode(IMaskR) << " nor " << explainOptCode(IER)
369 << " are set, one of the two is required.";
370 throw ChimeraTK::logic_error(oss.str());
371 }
372
373 /*----------------------------------------------------------------------------------------------------------------*/
374 // Throw if more than one entry of [MIE, GIE, MER] is there (test all combinations)
375 int nMieGieMer = static_cast<int>(optionRegisterSettings.test(MIE)) +
376 static_cast<int>(optionRegisterSettings.test(GIE)) + static_cast<int>(optionRegisterSettings.test(MER));
377 if(nMieGieMer > 1) {
378 std::ostringstream oss;
379 oss << "Invalid register " << JdkV1::OPTIONS_JSON_KEY
380 << " combination specified in map file json descriptor for GenericMuxedInterruptDistributor "
381 << controllerIDToStr(controllerID) << ": Only one out of " << explainOptCode(MIE) << ", "
382 << explainOptCode(GIE) << ", and " << explainOptCode(MER) << " can be set; " << std::to_string(nMieGieMer)
383 << " are set.";
384 throw ChimeraTK::logic_error(oss.str());
385 }
386
387 /*----------------------------------------------------------------------------------------------------------------*/
388 // Throw if unsupported options options received
389 if(optionRegisterSettings.test(IModeR)) { // REMOVE?
390 std::ostringstream oss;
391 oss << "Unsupported register " << JdkV1::OPTIONS_JSON_KEY
392 << " specified in map file json descriptor for GenericMuxedInterruptDistributor "
393 << controllerIDToStr(controllerID) << ": While " << explainOptCode(IModeR)
394 << " is a defined options in the AXI IntC v4.1 register space, it is not currently an allowed options in"
395 " the GenericMuxedInterruptDistributor";
396 throw ChimeraTK::logic_error(oss.str());
397 }
398
399 if(optionRegisterSettings.test(IVEAR) or optionRegisterSettings.test(IVAR) or optionRegisterSettings.test(IVR) or
400 optionRegisterSettings.test(ILR) or optionRegisterSettings.test(IModeR)) { // REMOVE?
401 std::ostringstream oss;
402 oss << "Unsupported register " << JdkV1::OPTIONS_JSON_KEY
403 << " specified in map file json descriptor for GenericMuxedInterruptDistributor "
404 << controllerIDToStr(controllerID) << ": While " << explainOptCode(ILR) << ", " << explainOptCode(IVR) << ", "
405 << explainOptCode(IVAR) << ", and " << explainOptCode(IVEAR)
406 << "are defined options in the AXI IntC v4.1 register space, they are not currently allowed options in the "
407 "GenericMuxedInterruptDistributor";
408 throw ChimeraTK::logic_error(oss.str());
409 }
410
411 if(optionRegisterSettings.test(IMaskR)) { // Temporary, this throw should be removed in a later version. TODO
412 std::ostringstream oss;
413 oss << "Unsupported register " << JdkV1::OPTIONS_JSON_KEY
414 << " specified in map file json descriptor for GenericMuxedInterruptDistributor "
415 << controllerIDToStr(controllerID) << ": " << explainOptCode(IMaskR)
416 << " is not currently an allowed options in the GenericMuxedInterruptDistributor, but should be supported "
417 "in a later version. ";
418 throw ChimeraTK::logic_error(oss.str());
419 }
420 /*----------------------------------------------------------------------------------------------------------------*/
421 // throw if ISR is not set. This should be impossible.
422 if(not optionRegisterSettings.test(ISR)) {
423 std::ostringstream oss;
424 oss << explainOptCode(ISR) << " is required but is not enabled for GenericMuxedInterruptDistributor "
425 << controllerIDToStr(controllerID);
426 throw ChimeraTK::logic_error(oss.str());
427 }
428 } // steriliseOptionRegisterSettings
429
430 /********************************************************************************************************************/
431 /********************************************************************************************************************/
432 /********************************************************************************************************************/
433
435 const boost::shared_ptr<SubDomain<std::nullptr_t>>& parent, const std::string& registerPath,
436 std::bitset<GmidOptionCode::OPTION_CODE_COUNT> optionRegisterSettings)
437 : MuxedInterruptDistributor(parent), _path(registerPath.c_str()) {
438 // Set required registers
439 optionRegisterSettings.set(ISR); // Ensure that the required option ISR is always set.
440 if(not optionRegisterSettings.test(IMaskR)) { // Ensure IMaskR or IER is on
441 optionRegisterSettings.set(IER);
442 }
443 // Note that we currently don't allowing IMaskR, but use of it gets caught later.
444
445 // Here we could explicitly note that we're ignoring IPR with optionRegisterSettings.reset(IPR)
446
447 /*----------------------------------------------------------------------------------------------------------------*/
448 steriliseOptionRegisterSettings(optionRegisterSettings, parent->getId());
449
450 /*----------------------------------------------------------------------------------------------------------------*/
451 _isr = _backend->getRegisterAccessor<uint32_t>(_path / getOptionRegisterStr(ISR), 1, 0, {});
452
453 _ierIsReallyImaskr = optionRegisterSettings.test(IMaskR);
454 _ier = _backend->getRegisterAccessor<uint32_t>(
456
457 // Set the clear/acknowledge register {ICR, IAR, ISR}
458 if(optionRegisterSettings.test(ICR)) { // Use ICR as the clear register
459 _icr = _backend->getRegisterAccessor<uint32_t>(_path / getOptionRegisterStr(ICR), 1, 0, {});
460 }
461 else if(optionRegisterSettings.test(IAR)) { // Use IAR as the clear register
462 _icr = _backend->getRegisterAccessor<uint32_t>(_path / getOptionRegisterStr(IAR), 1, 0, {});
463 }
464 else { // Use ISR to clear interrupts using its write 1 to clear feature
465 _icr = _backend->getRegisterAccessor<uint32_t>(_path / getOptionRegisterStr(ISR), 1, 0, {});
466 }
467
468 _hasMer = optionRegisterSettings.test(MER) or optionRegisterSettings.test(MIE) or optionRegisterSettings.test(GIE);
469 if(_hasMer) {
470 GmidOptionCode _optionMerMieGie =
471 optionRegisterSettings.test(MIE) ? MIE : (optionRegisterSettings.test(GIE) ? GIE : MER);
472 _mer = _backend->getRegisterAccessor<uint32_t>(_path / getOptionRegisterStr(_optionMerMieGie), 1, 0, {});
473 }
474
475 // steriliseOptionRegisterSettings ensures that either SIE and CIE are both enabled or neither are
476 _haveSieAndCie = optionRegisterSettings.test(SIE);
477 if(_haveSieAndCie) {
478 _sie = _backend->getRegisterAccessor<uint32_t>(_path / getOptionRegisterStr(SIE), 1, 0, {});
479 _cie = _backend->getRegisterAccessor<uint32_t>(_path / getOptionRegisterStr(CIE), 1, 0, {});
480 }
481
482 /*----------------------------------------------------------------------------------------------------------------*/
483 // Check register readability/writeability
484 // We have to check the catalogue, because this is the expected behaviour, so we
485 // are allowed to throw a logic error here.
486 // If the accessor actually behaves differently, it will cause a runtime error when used (which is expected
487 // behaviour and OK.) For map-file based backends the behaviour of catalogue and accessor should always be
488 // consistent.
489 auto catalogue = _backend->getRegisterCatalogue();
490
491 auto checkRedable = [catalogue](auto& accessor) {
492 auto description = catalogue.getRegister(accessor->getName());
493 if(!description.isReadable()) {
495 "GenericMuxedInterruptDistributor: Handshake register not readable: " + accessor->getName());
496 }
497 };
498 auto checkWriteable = [catalogue](auto& accessor) {
499 auto description = catalogue.getRegister(accessor->getName());
500 if(!description.isWriteable()) {
502 "GenericMuxedInterruptDistributor: Handshake register not writeable: " + accessor->getName());
503 }
504 };
505
506 checkRedable(_isr); // we only read from it
507 checkWriteable(_ier); // we only write to ier as we are the only user (otherwise we need sie and cie)
508 checkWriteable(_icr); // usually write only
509 if(_mer) {
510 // we only write, never read
511 checkWriteable(_mer);
512 }
513 if(_sie) {
514 checkWriteable(_sie); // usually write only
515 }
516 if(_cie) {
517 checkWriteable(_cie); // usually write only
518 }
519 } // constructor
520
521 /********************************************************************************************************************/
523 if(_backend->isFunctional()) {
524 try {
526 }
527 catch(ChimeraTK::logic_error& e) {
528 // This try/catch is just to silence the linter. ChimeraTK logic errors can always be avoided by checking the
529 // according pre-condition. We did so by checking isFunctional, so there should be no exception here.
530 std::cerr << "Logic error in ~GenericMuxedInterruptDistributor: " << e.what() << " TERMINATING!" << std::endl;
531 std::terminate();
532 }
533 }
534
535 } // destructor
536
537 /********************************************************************************************************************/
539 try {
540 _icr->accessData(0) = mask;
541 _icr->write();
542 }
544 }
545 }
546
547 /********************************************************************************************************************/
548 inline void GenericMuxedInterruptDistributor::clearOneInterrupt(uint32_t ithInterrupt) {
549 clearInterruptsFromMask(iToMask(ithInterrupt));
550 }
551
552 /********************************************************************************************************************/
556
557 /********************************************************************************************************************/
561
562 /********************************************************************************************************************/
564 _activeInterrupts &= ~mask;
565 try {
567 // IMaskR is used, so SIE and CIE are not defined.
568 _ier->accessData(0) = ~_activeInterrupts;
569 _ier->write();
570 }
571 else {
572 if(_haveSieAndCie) {
573 _cie->accessData(0) = mask;
574 _cie->write();
575 }
576 else {
577 _ier->accessData(0) = _activeInterrupts;
578 _ier->write();
579 }
580 }
582 }
584 }
585 }
586
587 /********************************************************************************************************************/
588 inline void GenericMuxedInterruptDistributor::disableOneInterrupt(uint32_t ithInterrupt) {
589 disableInterruptsFromMask(iToMask(ithInterrupt));
590 }
591
592 /********************************************************************************************************************/
594 /*
595 * - When creating an accessor to "!0:N" (or a nested interrupt "!0:N:M") //MIR = IMR = IMaskR.
596 * - if SIE and CIE are there, it writes ``1<<N`` to SIE and _clears_ with ``1<<N``
597 * - if IMR is there, it writes ~(``1<<N``) to IMR and _clears_ with ``1<<N``
598 * - if neither (SIE and CIE) nor MIR are present, or only IER is there, it writes ``1<<N`` to IER
599 * and _clears_ with ``1<<N``
600 * - When creating accessor "!0:L" while still holding "!0:N"
601 * - if SIE and CIE are there, it writes `1<<L` to SIE and to CIE
602 * - if IMR is there, it writes ~( (``1<<N``) | (`1<<L`) ) to MIR and _clears_ with `1<<L`
603 * - if neither (SIE and CIE) nor IMR are present, or only IER is there, it writes ( ``1<<N``)|(`1<<L`)
604 * to IER and _clears_ with `1<<L`
605 */
606 _activeInterrupts |= mask;
607 try {
608 if(_ierIsReallyImaskr) { // Set IMaskR in the form of IER
609 _ier->accessData(0) = ~_activeInterrupts;
610 _ier->write();
611 // When IMaskR is used, SIE and CIE cannot be defined.
612 }
613 else {
614 if(_haveSieAndCie) { // Set SIE
615 _sie->accessData(0) = _activeInterrupts;
616 _sie->write();
617 }
618 else { // Set IER, which actually is IER and not IMaskR
619 _ier->accessData(0) = _activeInterrupts;
620 _ier->write();
621 }
622 }
623 }
625 }
626 } // enableInterruptFromMask
627
628 /********************************************************************************************************************/
629 inline void GenericMuxedInterruptDistributor::enableOneInterrupt(uint32_t ithInterrupt) {
630 enableInterruptsFromMask(iToMask(ithInterrupt));
631 }
632
633 /********************************************************************************************************************/
635 try {
636 _isr->read();
637 uint32_t ipr = _activeInterrupts & _isr->accessData(0);
638
639 for(auto const& [i, subDomainWeakPtr] : _subDomains) {
640 // i is the bit index of the subDomain
641 if(ipr & iToMask(i)) {
642 if(auto subDomain = subDomainWeakPtr.lock(); subDomain) {
643 // The weak pointer might have gone.
644 // TODO FIXME: We need a cleanup function which removes the map entry.
645 // Otherwise we might be stuck with a bad weak pointer which is tried in each handle() call.
646
647 subDomain->distribute(nullptr, version);
648
649 // Requirement: nested interrupt handlers must clear their active interrupt flag first,
650 // then the parent interrupt flags are cleared.
652 }
653 }
654 } // for
655 }
657 // There's nothing to do. The transferElement part of _activeInterrupts has already called the backend's setException
658 }
659 } // handle
660
661 /********************************************************************************************************************/
662 std::unique_ptr<GenericMuxedInterruptDistributor> GenericMuxedInterruptDistributor::create(
663 [[maybe_unused]] std::string const& description, const boost::shared_ptr<SubDomain<std::nullptr_t>>& parent) {
664 /*
665 * This is a factory function. It parses the json, and calls the constructor.
666 * It returns an initalized GenericMuxedInterruptDistributor.
667 * 'description' is a JSON snippet containing configuration data
668 */
669
670 auto parseResult = parseAndValidateJsonDescriptionStrV0(parent->getId(), description);
671 std::bitset<OPTION_CODE_COUNT> optionRegisterSettings = parseResult.first;
672 std::string registerPath = parseResult.second;
673
674 return std::make_unique<GenericMuxedInterruptDistributor>(parent, registerPath, optionRegisterSettings);
675 } // create
676
677 /********************************************************************************************************************/
679 if(_hasMer) { // Set MER: turn on the Master Enable and HIE (hardware interrupt enable) bits
680 try {
681 _mer->accessData(0) = 0x00000003;
682 _mer->write();
683 }
685 }
686 }
687
688 // Disable any interrupts for which there is no valid subDomain.
689 uint32_t activeInterrupts = 0;
690 for(auto const& [i, subDomainWeakPtr] : _subDomains) {
691 try {
692 if(subDomainWeakPtr.lock()) {
693 activeInterrupts |= iToMask(i);
694 }
695 }
697 }
698 } // for
699 enableInterruptsFromMask(activeInterrupts);
700
702
703 // Activate all existing sub-domains. We have to implement a loop here because the parent activate is calling
704 // activateSubDomain() internally, which is not necessary because we already wrote to the hardware to do the
705 // handshake.
706 for(auto& subDomainIter : _subDomains) {
707 auto subDomain = subDomainIter.second.lock();
708 if(subDomain) {
709 subDomain->activate(nullptr, version);
710 }
711 }
712 } // activate
713
714 /********************************************************************************************************************/
716 SubDomain<std::nullptr_t>& subDomain, VersionNumber const& version) {
717 auto index = subDomain.getId().back();
718
721
722 subDomain.activate(nullptr, version);
723 }
724
725 /********************************************************************************************************************/
726} // namespace ChimeraTK::async
Class for generating and holding version numbers without exposing a numeric representation.
void clearInterruptsFromMask(uint32_t mask)
In mask, 1 bits clear the corresponding registers, 0 bits do nothing.
void enableInterruptsFromMask(uint32_t mask)
For each bit in mask that is a 1, the corresponding interrupt gets enabled, and the internal copy of ...
void disableInterruptsFromMask(uint32_t mask)
Disables each interrupt corresponding to the 1 bits in mask, and updates _activeInterrupts.
boost::shared_ptr< NDRegisterAccessor< uint32_t > > _ier
boost::shared_ptr< NDRegisterAccessor< uint32_t > > _cie
boost::shared_ptr< NDRegisterAccessor< uint32_t > > _isr
boost::shared_ptr< NDRegisterAccessor< uint32_t > > _sie
void handle(VersionNumber version) override
Handle gets called when a trigger comes in.
void activateSubDomain(SubDomain< std::nullptr_t > &subDomain, VersionNumber const &version) override
Function to activate a (new) single SubDomain if the MuxedInterruptDistributor is already active.
boost::shared_ptr< NDRegisterAccessor< uint32_t > > _icr
boost::shared_ptr< NDRegisterAccessor< uint32_t > > _mer
static std::unique_ptr< GenericMuxedInterruptDistributor > create(std::string const &description, const boost::shared_ptr< SubDomain< std::nullptr_t > > &parent)
Create parses the json configuration snippet 'description', and calls the constructor.
GenericMuxedInterruptDistributor(const boost::shared_ptr< SubDomain< std::nullptr_t > > &parent, const std::string &registerPath, std::bitset<(ulong) GmidOptionCode::OPTION_CODE_COUNT > optionRegisterSettings)
Interface base class for interrupt controller handlers.
std::map< size_t, boost::weak_ptr< SubDomain< std::nullptr_t > > > _subDomains
Send backend-specific asynchronous data to different distributors:
Definition SubDomain.h:33
std::vector< size_t > getId()
Definition SubDomain.h:53
void activate(BackendSpecificDataType, VersionNumber v)
Definition SubDomain.h:211
Exception thrown when a logic error has occured.
Definition Exception.h:51
const char * what() const noexcept override
Return the message describing what exactly went wrong.
Definition Exception.cpp:20
Exception thrown when a runtime error has occured.
Definition Exception.h:18
std::string explainOptCode(GmidOptionCode optCode)
This returns strings explaining the option code acronyms for use in error messages.
std::pair< std::bitset< OPTION_CODE_COUNT >, std::string > parseAndValidateJsonDescriptionStrV0(const std::vector< size_t > &controllerID, const std::string &descriptionJsonStr)
This extracts and validates data from the json snippet 'descriptorJsonStr' that matches the version 1...
std::string getOptionRegisterStr(GmidOptionCode optCode)
Given the Register option code enum, returns the corresponding string.
std::string strSetToStr(const std::set< std::string > &strSet, char delimiter=',')
The default delimiter is ',' TODO move this to some string helper library.
std::string controllerIDToStr(const std::vector< size_t > &controllerID)
Return a string describing the controllerID of the form "[1,2,3]".
std::string intVecToStr(const std::vector< size_t > &intVec, char delimiter=',')
Return a string describing the intVec of the form "1,2,3" The default delimiter is ',...
GmidOptionCode getOptionRegisterEnum(const std::string &opt)
If the string is not a recognized option code, returns GmidOptionCode::INVALID_OPTION_CODE It is not ...
uint32_t iToMask(const uint32_t ithInterrupt)
Return a 32 bit mask with the ithInterrupt bit from the left set to 1 and all others 0.
boost::bimap< std::string, GmidOptionCode > makeBimap(std::initializer_list< typename boost::bimap< std::string, GmidOptionCode >::value_type > list)
This is an initializer for a boost::bimap so that it can be produced using nice syntax.
void steriliseOptionRegisterSettings(std::bitset< OPTION_CODE_COUNT > const &optionRegisterSettings, std::vector< size_t > const &controllerID)
Ensures permissible combinations of option registers by throwing ChimeraTK::logic_error if there are ...
std::string to_string(const std::string &v)