PYNQ with interrupt from input pins

I want to use external input pins as Interrupt source, but without a need for peripheral blocks (e.g. AXI GPIO with INT enabled and its ip2intc_irpt signal - without it).
Instead: I am using the AXI Interrupt Controller block and connecting my input pins to it, able to configure falling_edge trigger, to enable and disable.
I need always the GPIO block with the ip2intc_irpt connected - otherwise Python do not have any interrupt signal.

Most important:
The Python handles just one single PL-to-PS interrupt, the IRQ_F2P[0:0]. OK.
But via the AXI Interrupt Controller registers I want to figure out which input pin has triggered the interrupt (read the ISR register). The AXI GPI block does not have such a feature: it triggers on any input w/o to figure out which one was it.

My issue:
It works fine as long as I have an AXI GPIO block and wire also the signal ip2intc_irpt. If I do not do this - the Python notebook does not find any interrupt signal.
Why?

How to use input pins as an interrupt source on AXI Interrupt Controller?

Even I set the type for all my inputs to “intr” - at the end no interrupt signals found by Python.

The only solution I have found:

  • have at least one “real” interrupt signal from an AXI GPIO Block connected to the AXI Interrupt Controller: just all input signals on AXI Interrupt Controller is OK on synthesis but not in Python
  • now combining all inputs as interrupt vector works
  • Just: I cannot disable the AXI GPIO interrupt and just use the other input wires. It does not work: it needs always the GPIO interrupt connected, enabled and used to trigger an interrupt.
    It looks to me also as: never mind if I enable/disable the GPIO interrupt and instead the other - Python seems to enable always the INT[0] (the GPIO INT) and uses only this one as a trigger.
    So, I can see the other input signals as INT triggered in AXI Interrupt Controller, but I cannot select just one of this as active. All my interrupts combined via AXI GPIO are always hot.

This approach has some drawbacks:

  1. I need just falling edge triggered interrupts. But the GPIO block triggers an INT on any change, on any edge, of any of the input signals.
  2. I have to bring all my input signals to the AXI GPIO input: otherwise, even connected on AXI Interrupt Controller - they are not seen as triggering.
  3. So, I get the INT handler triggered on any edge, TWICE, and I had to ignore the first one (just interested in falling edge, but AXI GPIO needs level triggered, otherwise it does not work).
  4. It is very important to clear the INT in the AXI GPIO as well as in the AXI Interrupt Controller!
    Do not forget to clear the INT in AXI GPIO when using the interrupt feature of it.

My block diagram with the interrupt wiring (using AXI GPIO):

The Yupyter Notebook of my INT handler in Python: watch the needed INT Clear functions called (for GPIO and for AXI Interrupt Controller):

I am thinking… Is this an issue with Python, e.g. parsing the HWH file and looking for specific names, name patterns related to INTs?
The HWH file looks reasonable, even not using AXI GPIO. Just the signal names are different.
The AMD Vivado is “happy” to generate the bit stream, just Python does not find any interrupt signal - if I do not use AXI GPIO and the INT output signal.

I can even use the concat output signal xlconcat_2/In0 in the Python script (as the reference for the interrupt signal to use).

Should I try to rename signal names so that Python sees an INT signal? (is it possible to give the output signal of the xlconcat_2 block the same name, e.g. as ip2intc_irpt (in order to make Python happy, and deselect INT on AXI GPIO)?

BTW: other issues seen:

  1. this asyncio.get_event_loop().run_forever() does not work: it does not get any interrupt triggered (it hangs forever)
  2. updating my block diagram, a new bit file generated (all fine), copied to PYNQ board but just restarting kernel and run all again - sometimes, it does not work: I have to reboot the entire system in order to get the updated block diagram.

renaming the output pin of concat does not work (read-only).

BTW:
the falling edge trigger logic in AXI Interrupt Controller works: I can configure to this falling edge, I see the ISR register set accordingly, just not clear how to use only these interrupt inputs in Python. At least: I can see which falling edge was there (but I need still the AXI GPIO interrupt signal for any edge seen as the main trigger).

It looks like, it triggers just on the AXI GPIO ip2intc_irpt signal, even I use the concat_2 output signal in Python.
What happens if I change the order on concat_2?
[11:0] now my input signals and [12:12] as ip2intc_irpt
(before the ip2intc_irpt was on the lowest vector input bit)

BTW: seen right now in Vivado warnings:
Even configuring the AXI Interrupt Controller to “manual” and setting falling edge for my signals: the input pins, created with type “intr” must be also configure for the same falling edge trigger.

Changing the inputs on concat before feeding the AXI Interrupt Controller does NOT work:
I have to make sure that the AXI GPIO ip2intc_irpt is the lowest bit on the vector.

"rrrr" - why it is so strange and complicated to use just input pins as INT source, without AXI GPIO needed???

My conclusion remains as this:

  1. You need at least one peripheral block with an INT output generated, e.g. AXI GPIO with this “ip2intc_irpt” signal via concat to the AXI Interrupt Controller
  2. This signal from AXI GPIO must be the first (lowest) on the concat block, to have it as the lowest INT signal vector bit
  3. Python seems to expect only this lowest INT signal, to configure it (on AXI Interrupt Controller?). Python acts just on this signal

To have all the other signals on AXI Interrupt Controller works: I can see the falling edges triggered, just Python does not take it as trigger (it needs always the AXI GPIO INT trigger).
So, I had to bring all input signals to the AXI GPIO (as INT source), just on AXI Interrupt Controller does not work.

It seems to be just a Python issue, not an issue with the Vivado bitstream file.
Does it mean? I should modify the Python LIB functions in order to support AXI Interrupt Controller for my input signal as interrupt source? (e.g. enabling other pins in AXI Interrupt Controller)

It remains at this “required” INT wiring:

How to use just and only my input pins as INT source?
How to get rid of using AXI GPIO block (which would trigger on any input transition change, on both edges, giving me two interrupts…)?
How to use the interrupt enable in AXI Interrupt Controller on any input signal?

Why Python does want to see this ip2intc_irpt signal wired and used?

So, it remains this:

  1. have AXI GPIO with interrupt enabled and signal ip2intc_irpt
  2. route all input signals to the AXI GPIO block (which should generate an INT)
  3. Bear in mind: now, any edge, any transition of any input signal generates an INT
  4. concat all input signals as well as the ip2intc_irpt to the AXI Interrupt Controller (via concat)
  5. the Python asyncio will just trigger on the single and FIRST INT signal: so, the ip2intc_irpt must be the lowest INT vector signal on AXI Interrupt Controller
  6. An INT is triggered by any signal change, never mind if enabled or disabled: the Python seems to use just the lowest INT vector bit on AXI Interrupt Controller (assuming it will configure/enable on AXI Interrupt Controller just the lowest input for being active)
  7. when an INT was triggered - you can read the AXI Interrupt Controller register ISR in order to figure out which one it was
  8. Don’t forget to CLEAR the INT in the AXI GPIO block as well as the AXI Interrupt Controller

It is a bit annoying that any signal transition triggers an INT (on both edges). So, my INT handler has to ignore one INT triggered (e.g. caused by a raising edge, even all my inputs are configured for falling edge). It comes from the fact:

  • that Python seems to use only the lowest bit on AXI Interrupt Controller inputs
  • and this is (and must be) the INT signal output from AXI GPIO - this signal seems to be the most important one and only used by Python. Bear in mind: this is and must be level triggered.
  • and every signal transition triggers an INT

OK, fine for now (even not happy with the Python implementation to handle INTs via AXI Interrupt controller).
My INT handler reads the AXI Interrupt Controller ISR register in order to realize which falling edge was there. Just: my INT handler is triggered twice (for both edges) and I had to waste time to discard the “spurious” INT.

My Jupyter notebook for the INT testing (two parts, continue from to the other):


It remains the question:
How to use the AXI Interrupt Controller with all the different inputs, to enable just one particular one, to get trigger in Python for this particular one…?
Even using AXI Interrupt Controller config, via the IER register, to select which one should trigger Python - it does not work (assuming Python acts just on the lowest INT input signal).

But it looks like: only bit 0 on AXI Interrupt Controller is enabled, managed, … by Python.
And this “trick” to route all my input pins as INT source via AXI GPIO is required to do.
(the AXI Interrupt Controller just left there to figure out in an easier way, which falling edge was there, the AXI GPIO does not tell me!)

Is there a way to modify the Python LIB files to make it working for “any” AXI Interrupt Vector input signal (at least to take my config done there into account)?