[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
- Help: NVIC, level-triggered interrupts and interrupt pending,
Igor Kotrasiński <=