7#include <nlohmann/json.hpp>
9#include <boost/bimap.hpp>
34 boost::bimap<std::string, GmidOptionCode>
makeBimap(
35 std::initializer_list<
typename boost::bimap<std::string, GmidOptionCode>::value_type> list) {
36 return {list.begin(), list.end()};
41 static const auto OptionCodeMap =
makeBimap({
67 inline uint32_t
iToMask(
const uint32_t ithInterrupt) {
68 return 0x1U << ithInterrupt;
81 auto it = OptionCodeMap.left.find(opt);
82 if(it != OptionCodeMap.left.end()) {
95 auto it = OptionCodeMap.right.find(optCode);
96 if(it != OptionCodeMap.right.end()) {
100 return "INVALID_OPTION_CODE";
111 static std::map<GmidOptionCode, std::string> mapOptCode2Message= {
112 {
ISR,
"Interrupt Status Register"},
113 {
IER,
"Interrupt Enable Register"},
114 {
MER,
"Master Enable Register"},
115 {
MIE,
"Master Interrupt Enable"},
116 {
GIE,
"Global Interrupt Enable"},
117 {
ICR,
"Interrupt Clear Register"},
118 {
IAR,
"Interrupt Acknowledge Register"},
119 {
IPR,
"Interrupt Pending Register"},
120 {
SIE,
"Set Interrupt Enable"},
121 {
CIE,
"Clear Interrupt Enable"},
123 "Interrupt Mask Register, not to be confused with Interrupt Mode Register"},
125 "Interrupt Mode Register, what AXI INTC v4.1 calls this 'IMR', not to be confused with Interrupt Mask "
126 "Register, which we call 'IMR'"},
127 {
IVR,
"Interrupt Vector Register"},
128 {
ILR,
"Interrupt Level Register"},
129 {
IVAR,
"Interrupt Vector Address Register"},
130 {
IVEAR,
"Interrupt Vector Extended Address Register"},
143 std::string
strSetToStr(
const std::set<std::string>& strSet,
char delimiter =
',') {
145 if(not strSet.empty()) {
146 for(
const auto& str : strSet) {
147 result += str + delimiter;
161 std::string
intVecToStr(
const std::vector<size_t>& intVec,
char delimiter =
',') {
163 if(not intVec.empty()) {
164 for(
const auto& i : intVec) {
191 const std::vector<size_t>& controllerID,
const std::string& descriptionJsonStr) {
204 static const std::vector<std::string> defaultOptionRegisterNames = {
"ISR",
"IER"};
205 std::string registerPath;
206 std::bitset<OPTION_CODE_COUNT> optionRegisterSettings(0);
208 nlohmann::json descriptionJson;
210 descriptionJson = nlohmann::json::parse(descriptionJsonStr);
212 catch(
const nlohmann::json::parse_error& ex) {
213 std::ostringstream oss;
215 <<
" was unable to parse map file json snippet " << descriptionJsonStr;
220 for(
auto& el : descriptionJson.items()) {
223 std::ostringstream oss;
224 oss <<
"Unknown JSON key '" << el.key() <<
"' provided to map file for GenericMuxedInterruptDistributor "
233 catch(
const nlohmann::json::exception& e) {
234 std::ostringstream oss;
236 <<
"' error for GenericMuxedInterruptDistributor " <<
controllerIDToStr(controllerID) <<
": " << e.what();
244 std::ostringstream oss;
245 oss <<
"GenericMuxedInterruptDistributor " <<
controllerIDToStr(controllerID) <<
" expects a "
252 catch(
const nlohmann::json::exception& e) {
253 std::ostringstream oss;
259 std::vector<std::string> optionRegisterNames;
264 catch(
const nlohmann::json::exception& e) {
265 std::ostringstream oss;
276 std::set<std::string> invalidOptionRegisterNamesEncountered;
277 for(
const auto& orn : optionRegisterNames) {
279 optionRegisterSettings.set(ornCode);
282 invalidOptionRegisterNamesEncountered.insert(orn);
285 if(!invalidOptionRegisterNamesEncountered.empty()) {
286 std::ostringstream oss;
287 oss <<
"Invalid register options " <<
strSetToStr(invalidOptionRegisterNamesEncountered)
293 return std::make_pair(optionRegisterSettings, registerPath);
303 std::bitset<OPTION_CODE_COUNT>
const& optionRegisterSettings, std::vector<size_t>
const& controllerID) {
318 if(optionRegisterSettings.test(
SIE) != optionRegisterSettings.test(
CIE)) {
319 std::ostringstream oss;
321 <<
" combination specified in map file json descriptor for GenericMuxedInterruptDistributor "
323 <<
" is set, but not both.";
328 if(optionRegisterSettings.test(
IMaskR)) {
329 if(optionRegisterSettings.test(
SIE)) {
330 std::ostringstream oss;
332 <<
" combination specified in map file json descriptor for GenericMuxedInterruptDistributor "
334 <<
" cannot not both be set.";
337 if(optionRegisterSettings.test(
CIE)) {
338 std::ostringstream oss;
340 <<
" combination specified in map file json descriptor for GenericMuxedInterruptDistributor "
342 <<
" cannot not both be set.";
348 if(optionRegisterSettings.test(
ICR) and optionRegisterSettings.test(
IAR)) {
349 std::ostringstream oss;
351 <<
" combination specified in map file json descriptor for GenericMuxedInterruptDistributor "
353 <<
" cannot not both be set.";
358 if(optionRegisterSettings.test(
IMaskR) and optionRegisterSettings.test(
IER)) {
359 std::ostringstream oss;
361 <<
" combination specified in map file json descriptor for GenericMuxedInterruptDistributor "
363 <<
" cannot not both be set.";
368 if(not(optionRegisterSettings.test(
IMaskR) or optionRegisterSettings.test(
IER))) {
369 std::ostringstream oss;
372 <<
" are set, one of the two is required.";
378 int nMieGieMer =
static_cast<int>(optionRegisterSettings.test(
MIE)) +
379 static_cast<int>(optionRegisterSettings.test(
GIE)) +
static_cast<int>(optionRegisterSettings.test(
MER));
381 std::ostringstream oss;
383 <<
" combination specified in map file json descriptor for GenericMuxedInterruptDistributor "
392 if(optionRegisterSettings.test(
IModeR)) {
393 std::ostringstream oss;
395 <<
" specified in map file json descriptor for GenericMuxedInterruptDistributor "
397 <<
" is a defined options in the AXI IntC v4.1 register space, it is not currently an allowed options in"
398 " the GenericMuxedInterruptDistributor";
402 if(optionRegisterSettings.test(
IVEAR) or optionRegisterSettings.test(
IVAR) or optionRegisterSettings.test(
IVR) or
403 optionRegisterSettings.test(
ILR) or optionRegisterSettings.test(
IModeR)) {
404 std::ostringstream oss;
406 <<
" specified in map file json descriptor for GenericMuxedInterruptDistributor "
409 <<
"are defined options in the AXI IntC v4.1 register space, they are not currently allowed options in the "
410 "GenericMuxedInterruptDistributor";
414 if(optionRegisterSettings.test(
IMaskR)) {
415 std::ostringstream oss;
417 <<
" specified in map file json descriptor for GenericMuxedInterruptDistributor "
419 <<
" is not currently an allowed options in the GenericMuxedInterruptDistributor, but should be supported "
420 "in a later version. ";
425 if(not optionRegisterSettings.test(
ISR)) {
426 std::ostringstream oss;
427 oss <<
explainOptCode(
ISR) <<
" is required but is not enabled for GenericMuxedInterruptDistributor "
439 std::bitset<GmidOptionCode::OPTION_CODE_COUNT> optionRegisterSettings)
442 optionRegisterSettings.set(
ISR);
443 if(not optionRegisterSettings.test(
IMaskR)) {
444 optionRegisterSettings.set(
IER);
461 if(optionRegisterSettings.test(
ICR)) {
464 else if(optionRegisterSettings.test(
IAR)) {
471 _hasMer = optionRegisterSettings.test(
MER) or optionRegisterSettings.test(
MIE) or optionRegisterSettings.test(
GIE);
474 optionRegisterSettings.test(
MIE) ?
MIE : (optionRegisterSettings.test(
GIE) ?
GIE :
MER);
492 auto catalogue =
_backend->getRegisterCatalogue();
494 auto checkRedable = [catalogue](
auto& accessor) {
495 auto description = catalogue.getRegister(accessor->getName());
496 if(!description.isReadable()) {
498 "GenericMuxedInterruptDistributor: Handshake register not readable: " + accessor->getName());
501 auto checkWriteable = [catalogue](
auto& accessor) {
502 auto description = catalogue.getRegister(accessor->getName());
503 if(!description.isWriteable()) {
505 "GenericMuxedInterruptDistributor: Handshake register not writeable: " + accessor->getName());
510 checkWriteable(
_ier);
511 checkWriteable(
_icr);
514 checkWriteable(
_mer);
517 checkWriteable(
_sie);
520 checkWriteable(
_cie);
539 std::cerr <<
"Logic error in ~GenericMuxedInterruptDistributor: " << e.
what() <<
" TERMINATING!" << std::endl;
584 pendingInterrupts &=
_isr->accessData(0);
593 std::cerr <<
"Watchdog: logic error reading ISR (device likely closed): " << e.
what() << std::endl;
599 if(pendingInterrupts == 0) {
603 _backend->setException(
"GenericMuxedInterruptDistributor: interrupt handler did not run for " +
605 " ms. Setting device exception.");
619 _icr->accessData(0) = mask;
647 _ier->accessData(0) = ~_activeInterrupts;
652 _cie->accessData(0) = mask;
688 _ier->accessData(0) = ~_activeInterrupts;
716 uint32_t processedMask = 0;
722 uint32_t newBits = ipr & ~processedMask;
723 for(
auto const& [i, subDomainWeakPtr] :
_subDomains) {
726 if(
auto subDomain = subDomainWeakPtr.lock(); subDomain) {
731 subDomain->distribute(
nullptr, version);
740 processedMask |= ipr;
746 std::this_thread::sleep_for(std::chrono::microseconds(20));
749 }
while((ipr & ~processedMask) != 0);
773 std::bitset<OPTION_CODE_COUNT> optionRegisterSettings = parseResult.first;
774 std::string registerPath = parseResult.second;
776 return std::make_unique<GenericMuxedInterruptDistributor>(parent, registerPath, optionRegisterSettings);
783 _mer->accessData(0) = 0x00000003;
791 uint32_t activeInterrupts = 0;
792 for(
auto const& [i, subDomainWeakPtr] :
_subDomains) {
794 if(subDomainWeakPtr.lock()) {
795 activeInterrupts |=
iToMask(i);
819 auto subDomain = subDomainIter.second.lock();
821 subDomain->activate(
nullptr, version);
829 auto index = subDomain.
getId().back();
834 subDomain.
activate(
nullptr, version);
Class for generating and holding version numbers without exposing a numeric representation.
void watchdogLoop()
watchdog thread.
std::atomic< bool > _handlerRan
Set true by handle() after each completed run.
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 ...
static constexpr std::chrono::milliseconds _watchdogInterval
Watchdog polling/decision interval (experimental)
~GenericMuxedInterruptDistributor() override
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
uint32_t _activeInterrupts
boost::shared_ptr< NDRegisterAccessor< uint32_t > > _cie
std::atomic< bool > _watchdogAlerted
Set true once the watchdog has raised a device exception.
boost::shared_ptr< NDRegisterAccessor< uint32_t > > _isr
std::atomic< bool > _stopWatchdog
boost::shared_ptr< NDRegisterAccessor< uint32_t > > _sie
void clearOneInterrupt(uint32_t ithInterrupt)
void clearAllEnabledInterrupts()
void handle(VersionNumber version) override
Handle gets called when a trigger comes in.
void activate(VersionNumber version) override
void activateSubDomain(SubDomain< std::nullptr_t > &subDomain, VersionNumber const &version) override
Function to activate a (new) single SubDomain if the MuxedInterruptDistributor is already active.
void clearAllInterrupts()
boost::shared_ptr< NDRegisterAccessor< uint32_t > > _icr
boost::shared_ptr< NDRegisterAccessor< uint32_t > > _mer
std::thread _watchdogThread
Watchdog thread and its stop flag.
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.
void enableOneInterrupt(uint32_t ithInterrupt)
void disableOneInterrupt(uint32_t ithInterrupt)
GenericMuxedInterruptDistributor(const boost::shared_ptr< SubDomain< std::nullptr_t > > &parent, const std::string ®isterPath, 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
boost::shared_ptr< DeviceBackend > _backend
Send backend-specific asynchronous data to different distributors:
std::vector< size_t > getId()
void activate(BackendSpecificDataType, VersionNumber v)
Exception thrown when a logic error has occured.
const char * what() const noexcept override
Return the message describing what exactly went wrong.
Exception thrown when a runtime error has occured.
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)
static constexpr const char *const VERSION_JSON_KEY
static constexpr const char *const OPTIONS_JSON_KEY
static constexpr const char *const PATH_JSON_KEY
static constexpr const int jsonDescriptorVersion