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