PYNQ: PYTHON PRODUCTIVITY

PYNQ interrupts

Hi,

I am attempting to get PYNQ asyncio interrupts working on an unsupported board with kernel and rootfs created with a 2020.1 PetaLinux flow.

from pynq import Overlay, GPIO, Interrupt
import asyncio
   
/usr/lib/python3.7/site-packages/pynq/pmbus.py:230: UserWarning: Could not initialise libsensors librarywarnings.warn("Could not initialise libsensors library")
ol=Overlay('min_intr_test.bit')
intc = ol.stream_intc
reset = GPIO(GPIO.get_gpio_pin(0), 'out')
gpio_trigger = GPIO(GPIO.get_gpio_pin(1), 'out')
ol.interrupt_pins

{'system_ila_0/probe1': {'controller': 'stream_intc',
    'index': 0,
   'fullpath': 'system_ila_0/probe1'},
   'stream_intc/intr': {'controller': 'stream_intc',
   'index': 0,
   'fullpath': 'stream_intc/intr'},
   'gpio_interrupt/Dout': {'controller': 'stream_intc',
   'index': 0,
   'fullpath': 'gpio_interrupt/Dout'}}
async def handle(interrupt):
    await interrupt.wait()
    print('handling interrupt')
    
async def trigger():
    await asyncio.sleep(2)    
    gpio_trigger.write(1)
    gpio_trigger.write(0);


intr_inst = Interrupt('stream_intc/intr')
handler_task = asyncio.ensure_future(handle(intr_inst))
trigger_task = asyncio.ensure_future(trigger())
trigger_task
<Task finished coro=<trigger() done, defined at <ipython-input-3-eb33238f6660>:5> result=None>
intc.register_map.ISR
Register(INT=1)

The trigger task fires and the interrupt controller registers the interrupt and fires off an IRQ but the handler task does not see it.

handler_task
<Task pending coro=<handle() running at <ipython-input-3-b5b1222e13f7>:2> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0xaec88810>()]>>

I read somewhere on these forums that instantiating the Interrupt class should not be an issue, but also see

Is this the naming of the interrupt controller stream_intc the issue? or is there something else I am missing?

All the code is looking reasonable. I would expect errors to be being thrown if there were issues with the device tree or interrupt controller naming - we’ve made the system more robust since that document was written.

A couple of questions:

Are you running this in Jupyter or on the command line? Do you have the code that runs the event loop?

It’s jupyter, the Xilinx fork of meta-jupyter through PetaLinux. .
The code in the OP is cut and paste from the notebook and complete, the loop code is handled by jupyter.

You might need to make sure that the future runs by using asyncio.get_event_loop().run_until_complete(trigger_task).

Peter

Yeah I tried that, but there is a problem with the 2020.1 Jupiter version, I think it lies upstream with twisted.

RuntimeError                              Traceback (most recent call last)
<ipython-input-5-2a2a5edde3e1> in <module>
     12 handler_task = asyncio.ensure_future(handle(intr_inst))
     13 trigger_task = asyncio.ensure_future(trigger())
---> 14 asyncio.get_event_loop().run_until_complete(trigger_task)

/usr/lib/python3.7/asyncio/base_events.py in run_until_complete(self, future)
    568         future.add_done_callback(_run_until_complete_cb)
    569         try:
--> 570             self.run_forever()
    571         except:
    572             if new_task and future.done() and not future.cancelled():

/usr/lib/python3.7/asyncio/base_events.py in run_forever(self)
    523         self._check_closed()
    524         if self.is_running():
--> 525             raise RuntimeError('This event loop is already running')
    526         if events._get_running_loop() is not None:
    527             raise RuntimeError(

RuntimeError: This event loop is already running

I’ll try from a plain command line script and get back to you. But my understanding is that launching the tasks into the running loop should work, but run does not.

You can use the nest-asyncio package to work around it. You can also try await trigger_task directly in the Jupyter cell.

Running this script should remove Jupyter from consideration

from pynq import Overlay, GPIO, Interrupt
import asyncio

ol=Overlay('min_intr_test.bit')
intc = ol.stream_intc
reset = GPIO(GPIO.get_gpio_pin(0), 'out')
gpio_trigger = GPIO(GPIO.get_gpio_pin(1), 'out')
ol.interrupt_pins


async def handle(interrupt):
    await interrupt.wait()
    print('handling interrupt')


async def trigger():
    await asyncio.sleep(2)
    gpio_trigger.write(1)
    gpio_trigger.write(0);


intr_inst = Interrupt('stream_intc/intr')
handler_task = asyncio.ensure_future(handle(intr_inst))
trigger_task = asyncio.ensure_future(trigger())
asyncio.get_event_loop().run_until_complete(trigger_task)

print(trigger_task)
print(handler_task)

same result as my OP

root@pynqpitaya:~# python3 min_interrupt_test.py
/usr/lib/python3.7/site-packages/pynq/pmbus.py:230: UserWarning: Could not initialise libsensors library
  warnings.warn("Could not initialise libsensors library")
<Task finished coro=<trigger() done, defined at min_interrupt_test.py:16> result=None>
<Task pending coro=<handle() running at min_interrupt_test.py:12> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0xb3a5f970>()]>>

What if you wait for the handler_task?

The loop never completes, and I have to ^c to get the shell back.

Can you see if any interrupts are appearing in /proc/interrupts? There should hopefully be a “fabric” line if you’re using the standard meta-xilinx-pynq

fabric is there

root@pynqpitaya:/proc# cat /proc/interrupts
           CPU0       CPU1
 16:          0          0     GIC-0  27 Edge      gt
 17:     223837     300343     GIC-0  29 Edge      twd
 18:          0          0     GIC-0  37 Level     arm-pmu
 19:          0          0     GIC-0  38 Level     arm-pmu
 20:         43          0     GIC-0  39 Level     f8007100.adc
 22:          0          0     GIC-0  57 Level     cdns-i2c
 24:          0          0     GIC-0  35 Level     f800c000.ocmc
 25:         36          0     GIC-0  59 Level     xuartps
 27:          0          0     GIC-0  58 Level     e0006000.spi
 28:          0          0     GIC-0  81 Level     e0007000.spi
 29:      10560          0     GIC-0  54 Level     eth0
 30:      16865          0     GIC-0  56 Level     mmc0
 31:          0          0     GIC-0  45 Level     f8003000.dmac
 32:          0          0     GIC-0  46 Level     f8003000.dmac
 33:          0          0     GIC-0  47 Level     f8003000.dmac
 34:          0          0     GIC-0  48 Level     f8003000.dmac
 35:          0          0     GIC-0  49 Level     f8003000.dmac
 36:          0          0     GIC-0  72 Level     f8003000.dmac
 37:          0          0     GIC-0  73 Level     f8003000.dmac
 38:          0          0     GIC-0  74 Level     f8003000.dmac
 39:          0          0     GIC-0  75 Level     f8003000.dmac
 40:       1310          0     GIC-0  40 Level     f8007000.devcfg
 42:          0          0     GIC-0  43 Level     ttc_clockevent
 48:          0          0     GIC-0  41 Edge      f8005000.watchdog
 49:          0          0     GIC-0  61 Level     fabric
IPI1:          0          0  Timer broadcast interrupts
IPI2:      16098      24931  Rescheduling interrupts
IPI3:          7          1  Function call interrupts
IPI4:          0          0  CPU stop interrupts
IPI5:          0          0  IRQ work interrupts
IPI6:          0          0  completion interrupts
Err:          0

Looks like the interrupts aren’t being detected by the PS. Would you be able to print the full register_map of the interrupt controller and also make sure it’s outputting a level-signalled interrupt rather than edge-signalled?

That could be the issue, the controller was set to edge for the processor IRQ.
I’ll rebuild the bitstream and let you know.

Yep that was it Peter, thanks for taking the time to track that down.

python3 min_interrupt_test.py
/usr/lib/python3.7/site-packages/pynq/pmbus.py:230: UserWarning: Could not initialise libsensors library
  warnings.warn("Could not initialise libsensors library")
handling interrupt
<Task finished coro=<trigger() done, defined at min_interrupt_test.py:16> result=None>
<Task finished coro=<handle() done, defined at min_interrupt_test.py:11> result=None>