This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

Generic Timer IRQ Handling

According to https://developer.arm.com/documentation/102379/0101/The-processor-timers?lang=en

"

The interrupts generated by the timer behave in a level-sensitive manner. This means that, once the timer firing condition is reached, the timer will continue to signal an interrupt until one of the following situations occurs:

  • IMASK is set to one, which masks the interrupt.
  • ENABLE is cleared to 0, which disables the timer.
  • TVAL or CVAL is written, so that firing condition is no longer met.

When writing an interrupt handler for the timers, it is important that software clears the interrupt before deactivating the interrupt in the GIC. Otherwise the GIC will re-signal the same interrupt again.
"

Does it mean we need a synchronization barrier (e.g. isb()) between two points:

P1. TVAL gets updated. The firing confition is no longer met.

-> ISB()

P2. ICC_EOIR, ICC_DIR


Or ISB() is not enough and we need something else there?

Thanks in advance

Parents
  • That's an interesting question!

    There is an extra thing to consider - latency.  The ISB can synchronise the change to the register, which (in this case) causes the timer's interrupt signal to be de-asserted.  There will be a finite delay between the CPU de-asserting the signal, the de-assertion being observed by the GIC, and the GIC update the Pending state.  You can check whether the GIC Redistributor (assuming GICv3?) has seen the change by reading back GICR_ISPENDR0.

    What if you execute ICC_DIR before the de-assert propagates?  

    The sequence we're expecting is:

    • Timer trigger
      • Asserts interrupt
      • INTID state: Inactive -> Pending
    • GIC forwards interrupt to code
    • Core takes exception and reads ICC_IAR
      • INTID state: Pending -> Active&Pending
    • Timer cleared
      • Deasserts interrupt
      • INTID state: Active&Pending -> Active
    • With to ICC_DIR
      • INTID state: Active -> Inactive

    Instead we'd get:

    • Timer trigger
      • Asserts interrupt
      • INTID state: Inactive -> Pending
    • GIC forwards interrupt to code
    • Core takes exception and reads ICC_IAR
      • INTID state: Pending -> Active&Pending
    • Timer cleared
      • Deasserts interrupt
    • With to ICC_DIR
      • INTID state: Active&Pending -> Pending
    • GIC observes change in timer interrupt signal
      • INTID state: Pending -> Inactive

    There's a small chance that the GIC could "re-signal" the interrupt between the last two steps.  But I think it very unlikely.  The reason is for this situation to happen the deactivate (due to the write to ICC_DIR) has to propagate to the GIC faster than the change in the timer interrupt signal.  The timer interrupt signal is a wire.  The deactivate goes over a command bus using GIC Stream Protocol.

    So if you care, then software can poll the GICx_ISPENDR<n> register.  If you don't poll that register, you run a small chance of getting a spurious interrupt, which typically software can cope with.

Reply
  • That's an interesting question!

    There is an extra thing to consider - latency.  The ISB can synchronise the change to the register, which (in this case) causes the timer's interrupt signal to be de-asserted.  There will be a finite delay between the CPU de-asserting the signal, the de-assertion being observed by the GIC, and the GIC update the Pending state.  You can check whether the GIC Redistributor (assuming GICv3?) has seen the change by reading back GICR_ISPENDR0.

    What if you execute ICC_DIR before the de-assert propagates?  

    The sequence we're expecting is:

    • Timer trigger
      • Asserts interrupt
      • INTID state: Inactive -> Pending
    • GIC forwards interrupt to code
    • Core takes exception and reads ICC_IAR
      • INTID state: Pending -> Active&Pending
    • Timer cleared
      • Deasserts interrupt
      • INTID state: Active&Pending -> Active
    • With to ICC_DIR
      • INTID state: Active -> Inactive

    Instead we'd get:

    • Timer trigger
      • Asserts interrupt
      • INTID state: Inactive -> Pending
    • GIC forwards interrupt to code
    • Core takes exception and reads ICC_IAR
      • INTID state: Pending -> Active&Pending
    • Timer cleared
      • Deasserts interrupt
    • With to ICC_DIR
      • INTID state: Active&Pending -> Pending
    • GIC observes change in timer interrupt signal
      • INTID state: Pending -> Inactive

    There's a small chance that the GIC could "re-signal" the interrupt between the last two steps.  But I think it very unlikely.  The reason is for this situation to happen the deactivate (due to the write to ICC_DIR) has to propagate to the GIC faster than the change in the timer interrupt signal.  The timer interrupt signal is a wire.  The deactivate goes over a command bus using GIC Stream Protocol.

    So if you care, then software can poll the GICx_ISPENDR<n> register.  If you don't poll that register, you run a small chance of getting a spurious interrupt, which typically software can cope with.

Children