qemu-arm
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Help: NVIC, level-triggered interrupts and interrupt pending


From: Igor Kotrasiński
Subject: Help: NVIC, level-triggered interrupts and interrupt pending
Date: Mon, 30 May 2022 11:33:32 +0200
User-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:91.0) Gecko/20100101 Thunderbird/91.1.2

Hi,

I've been hacking on QEMU recently, adding support for a custom Cortex-M 
board. One of the devices that I'm emulating is using level-triggered 
interrupts, for which I assume qemu_irq_raise/lower is the right tool. 
However, I'm having trouble receiving interrupts for my device which I 
*think* are caused by interaction with setting/clearing 
interrupt-pending status.

The way I'm handling my interrupt currently is as follows:

1. The device raises an interrupt with qemu_irq_raise.
2. In interrupt handler, I set the clear-enable nvic register and queue 
a handler.
3. In handler, I read data from the device and set a register that 
lowers its interrupt with qemu_irq_lower.
4. Then I set the clear-pending nvic register.
5. Finally I set the set-enable nvic register.

The problem happens when the device re-raises the interrupt between 
steps 3 and 4. I would expect the interrupt to stay pending after 
clearing its pending status, and random ARM docs I found on the net seem 
to confirm that:

     A pending interrupt remains pending until one of the following:
     ...
     * Software writes to the corresponding interrupt clear-pending 
register bit. For a level-sensitive interrupt, if the interrupt signal 
is still asserted, the state of the interrupt does not change.

 
https://developer.arm.com/documentation/dui0497/a/cortex-m0-peripherals/nested-vectored-interrupt-controller/level-sensitive-and-pulse-interrupts

On QEMU however, pending status is cleared and I lose the interrupt. I 
tried to hack in the behaviour described in the docs quoted above, like so:

diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c
index 773e311754..4914b1217f 100644
--- a/hw/intc/armv7m_nvic.c
+++ b/hw/intc/armv7m_nvic.c
@@ -2393,6 +2393,12 @@ static MemTxResult nvic_sysreg_write(void 
*opaque, hwaddr addr,
          for (i = 0, end = size * 8; i < end && startvec + i < 
s->num_irq; i++) {
              if (value & (1 << i) &&
                  (attrs.secure || s->itns[startvec + i])) {
+                /* If the interrupt signal is still asserted, the state 
of the
+                 * interrupt does not change.
+                 */
+                if (!setval && s->vectors[i + NVIC_FIRST_IRQ].level) {
+                    continue;
+                }
                  s->vectors[startvec + i].pending = setval;
              }
          }

This fixed the issue I was having. However, I'm not sure if it's the 
right solution. One, this affects all devices connected to nvic, so this 
might randomly break them. Two, there's a confusing comment in 
armv7m_nvic.c, in set_irq_level, that seems to imply that all external 
interrupts are edge-triggered:

     /* The pending status of an external interrupt is
      * latched on rising edge and exception handler return.
      *
      * Pulsing the IRQ will always run the handler
      * once, and the handler will re-run until the
      * level is low when the handler completes.
      */

I'd be very thankful for any help with this. I *think* I found a bug, 
but I'm not 100% sure.

Thanks,
Igor



reply via email to

[Prev in Thread] Current Thread [Next in Thread]