This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Atomic IRQ enable/disable?

Hi,

I was looking at the code for enabling/disabling interrupts and I'm using code like in this post:

http://e2e.ti.com/support/microcontrollers/hercules/f/312/t/100109.aspx#353088

I'm wondering if there are any potential issues with mixing the usage of these macros in interrupt and non-interrupt code execution or any other potential hazards regarding the CPSR state.  In our system, we are only using supervisor mode and we never enter user mode.  So we call these _Enable_Irq() and _Disable_Irq() functions from normal code not running in interrupts.  I see that there is a read/modify/write in these functions, so there is a potential for an interrupt to occur and change the CPSR after the read instruction, so that after the interrupt finishes, we are now writing back a potentially wrong value to the CPSR.  Is this something that can happen in the system as I have described?

Thanks,

Marco

  • Hi Marco,

    You can use the "Change Processor State" (CPS) assembly instruction instead of the read-modify-write macros to enable or disable interrupts. For example,

    CPSIE I, F ; enable IRQ, FIQ

    CPSIE I ; enable iRQ only

    CPSIE F ; enable FIQ only

    CPSID I ; disable IRQ

    The CPS instruction also completes in a single cycle versus the 5 cycles required for the MSR (on top of the read and modify instruction cycles).

    Regards, Sunil

  • Hi Sunil,
    is it possible to use this instructions in User Mode?
    Best regards
    Falk
  • Falk,

    No. This is a System Instruction. The document that explains this is ARM DDI 0406 "ARM Architecture Reference Manual ARMv7-A and ARMv7-R".

    If you are in User Mode - then you have to switch back to a privileged mode by an SVC instruction. The SVC handler needs to change the processor state for your and return.

    Or you can use System mode instead of user mode, but you lose some of the protection you get (i.e. MPU) if you do this.
  • Hi Anthony,

    thanks for the answer.
    Is there another way on RM42 to disable both, the pin-interrupt and the rti-interrupt at the atomic level (thus at the same time)?

    Best regards

    Falk

  • Hi Falk,

    So to be clear, this new question is can you selectively mask just 2 of the interrupt sources at the same time (atomically) ... not a global IRQ disable like CPS would cause?

    Answer then is probably a 'yes' ... I say probably because you can disable multiple interrupt channels in the VIM with a single write, but they have to be within the same 32-bit register. Since there are more than 32 VIM channels ... it'll depend on how you have the mapping of interrupt requests from peripherals to VIM channels configured. If you put your two interrupt requests that you want to coordinate within the same 32-bit register, I think you can mask them both simultaneously.
  • Hi Anthony,

    I want to disable/enable 2 the Pin-Irq and the RTI-Irq at the same time in USER-Mode!
    I could write the VIM-Register, but only in privileged mode.

    Is there a way?

    Thanks!

  • Hi Falk,
    If the register has a "P" under the bit field description in the TRM, then yes you will need to be in privileged mode in order to write to the register. So the procedure would be the same as the global IRQ case, you would have to use the SVC call and have an SVC handler that actually performs the function for you at a privilege level of operation.
  • Thanks Anthony.

    Can you give a brief example for such a svc call in assembler?

    I took this one from somewhere, it works but is this all needed?

    _svc
    	PUSH     {R0-R3,R12,lr}       ; Store registers.
    	MOV      R1, sp               ; Set pointer to parameters.
    	MRS      R0, SPSR             ; Get SPSR.
    	PUSH     {R0,R3}              ; Store SPSR onto stack and another register to maintain
    									  ; 8-byte-aligned stack. Only required for nested SVCs.
    	TST      R0,#0x20             ; Occurred in Thumb state?
    	LDRHNE   R0,[lr,#-2]          ; Yes: load halfword and...
    	BICNE    R0,R0,#0xFF00        ; ...extract comment field.
    	LDREQ    R0,[lr,#-4]          ; No: load word and...
    	BICEQ    R0,R0,#0xFF000000    ; ...extract comment field.
    									  ; R0 now contains SVC number
    									  ; R1 now contains pointer to stacked registers
    	BL       SVC_handler          ; Call C routine to handle the SVC.
    	POP      {R0,R3}              ; Get SPSR from stack.
    	MSR      SPSR_cf, R0          ; Restore SPSR.
    	LDM      sp!, {R0-R3,R12,pc}^ ; Restore registers and return.

    Best regards

    Falk

  • Hi Falk,

    The following code is copied from a App Note which I wrote recently and which will be released soon.
    It will talk about the interrupt and abort system as implemented on the Hercules devices.

    How to declare SVC functions with the TI tool chain:

           #pragma SWI_ALIAS(unimplementedSVC,     0);
           #pragma SWI_ALIAS(switchCpuMode,        1);
           #pragma SWI_ALIAS(switchToSystemMode,   2);
           #pragma SWI_ALIAS(switchToUserMode,     3);
           #pragma SWI_ALIAS(writePrivRegister32, 33);
           #pragma SWI_ALIAS(writePrivRegister8,  34);
           #pragma SWI_ALIAS(testReentrantSVC,    35);
    
           void     unimplementedSVC(void); /* Used to test fault handler */
           uint32_t switchCpuMode(uint32_t u32ModeNum);
           void     switchToSystemMode(void);
           void     switchToUserMode(void);
    
           /* The following SVC functions are implemented at C level */
           void     writePrivRegister32(uint32_t * pu32Address, uint32_t u32Value);
           void     writePrivRegister8(uint8_t * pu8Address, uint8_t u8Value);
           void     testReentrantSVC(uint32_t * pu32Address, uint32_t u32Repetition);

    _svc.asm

    ;-------------------------------------------------------------------------------
    ; SWI wrapper
            .global     c_svc
            .global     _svc
            .text
            .arm
            .armfunc    _svc
    
       .align 4
    _svc:
            .asmfunc
            ; Preserver A1 and A2 these may hold parameters to the function which are needed in C level handler
            ; Note: This handler doesn't preserve callee saved (Save-on-call) registers, A1 to A4 and V9.
            ;       In other words the callee function has to preserve them which is ensured when function like SVC is used in C (#pragma SWI_ALIAS() or __svc())
            ;       Take care when assembly inlining SVC / SWI.
    
            MRS     A4, SPSR            ; Get spsr
    
            TST     A4, #0x20           ; Called in Thumb state?
    
            ; Note: When called from Thumb code only 256 unique SVC handlers can be distungished, as the Thumb SVC instruction has only a 8 bit field.
            ;       When called from ARM code 2^24 unique SVC handlers can be distungished, as the ARM SVC instruction has a 24 bit field.
    
            LDRNEH  A3, [lr,#-2]        ; Yes: Load halfword and...
            BICNE   A3, A3, #0xFF00     ; ...extract comment field
            LDREQ   A3, [lr,#-4]        ; No: Load word and...
            BICEQ   A3, A3, #0xFF000000 ; ...extract comment field
                                        ; r2/A3 now contains SVC number
                                        ; r3/A4 now contains SPSR (Saved Program Status Register)
    
            CMP     A3, #32
            BHI     _default            ; Branch if higher
    
    		LDRLS   pc, [pc, A3, LSL #2]; Load address from table
    
    		.word   0x00
    
    _table: .word   (_case0)  ; unimplementedSVC
            .word   (_case1)  ; switchCpuMode
            .word   (_case2)  ; switchToSystemMode
            .word   (_case3)  ; switchToUserMode
            .word   (_case4)
            .word   (_case5)
            .word   (_case6)
            .word   (_case7)
            .word   (_case8)
            .word   (_case9)
            .word   (_case10)
            .word   (_case11)
            .word   (_case12)
            .word   (_case13)
            .word   (_case14)
            .word   (_case15)
            .word   (_case16)
            .word   (_case17)
            .word   (_case18)
            .word   (_case19)
            .word   (_case20)
            .word   (_case11)
            .word   (_case22)
            .word   (_case23)
            .word   (_case24)
            .word   (_case25)
            .word   (_case26)
            .word   (_case27)
            .word   (_case28)
            .word   (_case29)
            .word   (_case30)
            .word   (_case31)
            .word   (_case32)
    
            .word   0x00
    
    _case0: ; unimplementedSVC (used to test fault handler)
            B       _default
    
    _case1: ; switchCpuMode
            AND     A2, A1, #0x0000001F ; Ensure that only mode bits are in A1
            AND     A1, A4, #0x0000001F ; Store mode on entry in R0/A1 to return it to callee
            BIC     A4, A4, #0x0000001F ; Clear Mode bits
            ORR     A4, A4, A2          ; Set Mode bits as in A2 (former A1)
            MSR     SPSR_cxsf, A4       ; Restore spsr
            B       _exit_svc           ; Branch to exit handler
    
    _case2: ; switchToSystemMode
            ;BIC     A4, A4, #0x0000001F
            ORR     A4, A4, #0x0000001F ; Set bits fro System Mode (M0-M4 are set)
            MSR     SPSR_cxsf, A4       ; Restore spsr
            B       _exit_svc
    
    _case3: ; switchToUserMode
            BIC     A4, A4, #0x0000001F ; Clear Mode bits
            ORR     A4, A4, #0x00000010 ; Set Mode Bits for User Mode
            MSR     SPSR_cxsf, A4       ; Restore spsr
            B       _exit_svc
    
    _case4:
    _case5:
    _case6:
    _case7:
    _case8:
    _case9:
    _case10:
    _case11:
    _case12:
    _case13:
    _case14:
    _case15:
    _case16:
    _case17:
    _case18:
    _case19:
    _case20:
    _case21:
    _case22:
    _case23:
    _case24:
    _case25:
    _case26:
    _case27:
    _case28:
    _case29:
    _case30:
    _case31:
    _case32:
            B       _default
    
    _default:
            ; A4 holds SPSR
            STMFD   SP!, {A4, LR}    ; Store SPSR and LR on Stack
    
            BL      c_svc            ; Call _TI_SWI
    
            LDMFD   SP!, {A4, LR}    ; Get SPSR and LR from stack
            MSR     SPSR_cxsf, A4    ; Restore spsr
    
    _exit_svc:
            MOVS PC, LR              ; Return from Exception
    
            .endasmfunc
    
            .end
    

    svc.c

    #include "sys_common.h"
    #include "svc.h"
    
    #ifdef DEBUG
    #include <stdio.h>
    #endif
    
    extern uint32_t c_svc(uint32_t u32Param1, uint32_t u32Param2, uint32_t u32Handler);
    
    static void testReentrantSvcInThumb(uint32_t * pu32Param1, uint32_t u32Param2)
    {
    	if (0ul != u32Param2)
    	{
    		testReentrantSVC(pu32Param1 + 1, u32Param2 - 1ul); /* Call function recursively */
    
    		writePrivRegister32(pu32Param1, (uint32_t)pu32Param1);
    	}
    
    	return;
    }
    
    uint32_t c_svc(uint32_t u32Param1, uint32_t u32Param2, uint32_t u32Handler)
    {
    	uint32_t u32ReturnVal = 0ul;
    
    #ifdef DEBUG
    	printf("Called C Level SVC Handler #%u\n", u32Handler);
    #endif
    
    	switch(u32Handler)
    	{
    	case 33: /* writePrivRegister32() */
    		u32ReturnVal           = *(uint32_t *)u32Param1;
    		*(uint32_t *)u32Param1 = u32Param2;
    		break;
    
    	case 34: /* writePrivRegister8() */
    		u32ReturnVal          = (uint32_t)(*(uint8_t *)u32Param1);
    		*(uint8_t *)u32Param1 = (uint8_t)u32Param2;
    		break;
    
    	case 35: /* testReentrantSVC */
    		testReentrantSvcInThumb((uint32_t *)u32Param1, u32Param2);
    		break;
    
    	default:
    		ASSERT(0);
    		break;
    	}
    
    	return u32ReturnVal;
    }
    

    Please treat the code snippets as pure examples, we haven't yet went through the final review process and they might be faulty.
    You will find further useful hints how to write these kinds of abort handlers in the following ARM document: ARM® Compiler tool chain – Developing Software for ARM Processors (ARM DUI 0471)

    Note: Sunil just gave me the hint that the switch to User Mode could be easily achieved with a single CPS #16 instruction instead of an rather complex SWI handler. So please consider a CPS instruction to switch from any privileged mode to User Mode instead of using this SWI handler, it will save you a lot of cycles.

    Best Regards,
    Christian