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

M4 Assembly - Set Enable also enables the Clear Enable Interrupt Register

Hi,

I have some assembly for Cortex M4 (Arm 7M Thumb), I want to enable an interrupt that is connected to a push button on an STM32 F407. It works, but for some reason when I enable the set enable register, the clear enable register also gets set ? Is this normal ?

NVIC_BASE           EQU     0xE000E100
NVIC_ISER0          EQU     NVIC_BASE
SETENA6             EQU     1 << EXTI0_IRQn

EnableButtonINT PROC
    ;Enables the interrupt for the blue pushbutton
    LDR     r0, =NVIC_ISER0         ;set enable interrupt from EXTI0_IRQn channel
    LDR     r1, =SETENA6
    STR     r1, [r0]
    BX      LR
    ENDP   

So the code above results in 0x40 on set enable AND clear enable registers !

The reason I ask is that I seem to be getting two interrupts all the time. I've put a debounce on so my flow is

1. Enable interrupts

2. On button interrupt - toggle LED, disable button interrupt (for debounce), use the systick timer to get another interrupt in 200ms

3. On systick interrupt (200 ms after button press), re-enable button interrupt again ie debounce finished

So I'm disabling the button interrupt to prevent repeated button interrupts when user is really only pressing ovce (typical debounce). But I get an interrupt for button on, then when the re-enable occurs I get another one straight away (I've cleared enabled and pending).

  • Reading the CLEAR ENABLE register returns "1" in the bit positions of the interrupts that are currently enabled, so it sounds like it is behaving as it's supposed to.

    From the ARM v7m ARM:

    CLRENA, bits[m]

    B3 System Address Map B3.4 Nested Vectored Interrupt Controller, NVIC

    For register NVIC_ICERn, disables or shows the current enabled state of interrupt (m+(32*n)):

    0 On reads, interrupt disabled. On writes, no effect.

    1 On reads, interrupt enabled. On writes, disable interrupt.

  • If you get two interrupts all the time, the reason might be caused by a race condition between clearing of interrupt at the peripheral and the interrupt return (end of interrupt service routine, ISR).

    Please try add a dummy read/write to the peripheral after clearing the interrupt request to see if it solve the problem. If it does, it means the peripheral bus bridge has a write buffer that cause a delay between the write that clear the interrupt at the peripheral, to the time that the interrupt request is actually cleared. 

    Please see http://www.keil.com/support/docs/3928.htm for more information.

  • Thanks for the reply, just finished reading your book actually on the M3 and M4 (very good). If you dont mind I have a few more questions on interrupts,

    1. Why are there 2 regs for interrupts ie set enable and clear ? Set enable  and clear enable could just be done with a single register couldn't it ? Set enble - we set the bit to 1 and the status of the int is enabled, or we unset the bit to 0 and its disabled. Why have a seperate clear
    2. Also When an interrupt is activated and the ISR gets hit, does just returning from the ISR clear the active interrupt being processed (current active) ?
    3. What happens if you disable an interrupt, does it clear any pending interrupts waiting to get processed of that type ? Or can you get an ISR execution on an interrupt that you just disabled becasue it was peding when you disabled it(and has therefore not been processed yet) ?

    Thanks again Joseph.

  • OK thanks, I have a few more questions on interrupts that you might be able to help with, I think what I'm confused about is why there are 2 registers for active interrupts - set enable and clear enable. I can see them as mutually exclusive, so why not just use a single register ?

  • Having separate set and clear registers avoid some race conditions where the interrupt enable/disable are modified inside ISRs. Without separate registers, you need read-modify-write (RMW) to change a register, which can lead to the following scenarios:

    - ISR X read interrupt enable register

    - ISR X modify the enable bit in software (not write back yet)

    - IRQ Y (higher priority) arrived and pre-empted

    - ISR Y modify its enable control by another RMW

    - ISR Y exit and returning to ISR X

    - ISR X write back enable control, which over-written the modification done by ISR Y.

    With separated SET and CLEAR, you can modify the control using a single write, hence avoided this RMW race condition.

    Your question number 2 is not very clear - I guess you mean what happen if a new IRQ event arrive when the ISR is running? The interrupt pending status is clear as it entered the ISR, and when the IRQ event arrive again during the execution of ISR, then the pending status is pended again. So when the ISR exit, the pending status will cause the processor to handle the interrupt again.

    Regarding 3

    Disabling interrupt (clearing enable, excluding special cases of using PRIMASK/FAULTMASK/BASEPRI) does not clear the pending status, and it is possible for the interrupt to be trigger just at the same time. This is documented in one of the application notes:

    http://infocenter.arm.com/help/topic/com.arm.doc.dai0321a/BIHHFHJD.html

    regards,

    Joseph

  • Thanks for the answers. I can see now that a seperate set and clear allows the the changing of the interrupt state to be atomic and therefore prevent a pre-emption from causing a race condition by stale read/modify/write.

    The biggest problem I seem to be having is when I clear the active interrupt it seems to set up another pending interrupt sometimes (this can be affected by debugging). This seems to be made less frequent by adding a DSB after I clear pending in my ISR (as the ISR clears pending and disables interrupt). Also for my debounce when systick interrupt ISR has been called many times (to give me a 500ms delay), when I re-enable interrupt I sometimes get another pending. Pending interrupts seem to be pretty flaky, and I seem to have spent 80% of my development time for this small app debugging them. I really think disabling an interrupt should clear active and pending of that type also, I understand this would not be able to clear any current active interrupt that is currently executing and has been pre-empted, but if I'm turning it off then I really don't want any ISR to be exectued for it. I can't turn off all interrupt (global state), as I'm using the systick interrupt for a debounce expiration notification.