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.

Intrinsic function or inline assebly for setting the BASEPRI register in TI CCS C/C++ compiler



My question is about the most efficient and safe way to set the BASEPRI register to mask interrupts above a certain level in the TI CCS C/C++ compiler. I've searched the documentation, but I could not find any intrinsic function to do this. I'm reluctant to use naive inline assembly, because the BASEPRI register must be set by means of the MSR instruction, which in turn requires clobbering a register. As far as I know, there is no way of telling the compiler that the asm instruction has clobbered a register (as it is in the GCC compiler, for example).

Currently, the only option I see is to call a function defined in assembly. But this breaks the pipeline twice, which uses many more CPU cycles than the actual setting of the BASEPRI register.

The other ARM compilers for Cortex-M, such as Keil, IAR and GNU, allow setting the BASEPRI register inline with just two instructions. It would be nice if the TI compiler could match it.

Any help would be greatly appreciated.

  • Hi Miro,

         See, if setting the BASEPRI register can be done using the HWREG macro. HWREG macro can be found at "\inc\hw_types.h".

    -kel

  • If you mean the HWREG() macro from Stellarisware, it certainly won't do. The BASEPRI register can be accessed only via MSR/MRS instructions.

  • Hi Miro,

    Did you look at the CPUbasepriSet() API in cpu.c module under "driverlib/" folder? I am not sure if this is the most efficient method of setting the Basepri register but it is definitely safe.

    Sai
  • Yes, as I said in the OP, I realize that I can always code a function in assembly. (The function CPUbasepriSet() is implemented for the CCS compiler as __asm("    msr BASEPRI, r0\n"); ). But executing this function requires a 4-byte BL instruction, which takes at least 2 clocks to execute. Then you break the pipeline, which costs 3-4 clocks. Then you execute the MSR BASEPRI,r0 instruction, which is 1 clock. Then you execute BX lr, which is at least 1 clock. Then you break the pipeline again, which costs 3-4 clocks. In summary, you take at least 10-12 clocks for something that should take 2 (80% overhead).

    I realize that this is probably in the noise for most situations, but when you try to write an optimized kernel-type code, all this adds up, because you disable and enable interrupts quite often. In other words, the efficiency of interrupt disabling and enabling goes directly into the task-level response of the kernel.

    In the end, for anybody interested in the efficient code, I've implemented setting the BASEPRI register as the following macro:

        #define SET_BASEPRI(val) __asm( \
            " PUSH {R0}\n\r" \
            " MOV  R0,#" #val "\n\r" \
            " MSR  BASEPRI,R0\n\r" \
            " POP  {R0}")
    

    Please note that the clobbered register is pushed and popped from the stack. This overhead is still smaller than calling a function and returning from a function. I estimate that this code should take about 6 cycles to execute.

    With the SET_BASEPRI() macro, a critical section can be implemented as:

    SET_BASEPRI(0x3F); // enter critical section
    // inside critical section
    SET_BASEPRI(0); // exit critical section 

    Anyway, this is the best I could do in the current version of the CCS compiler. 

    But the real solution should be adding an intrinsic function to the compiler. Is anyone from TI listening???

  • I may be showing my age but my immediate reaction is not "this should be an intrinsic" but "why are you not doing this in assembler?"

    Robert

  • The whole purpose of the BASEPRI register in ARM Cortex-M3/M4 is to allow disabling interrupts selectively. In other words, this is a method to implement critical sections in code. And by "code" I mean mostly C or C++ code.

    Unless, of course, you program your entire project in assembly...

  • Hi Miro,

    I'm trying to port the QP-examples (e.g. Blinky) to MSP432 which is a Cortex-M4F-core processor and found this post since I was missing exactly the functionality you have asked about (for implementing QF_INT_ENABLE).

    I need to stick to CCS with the TI compiler but could not find any examples for this compiler and MSP432. Have you made any progress porting the examples to the TI compiler or do you know of anybody who succeeded in it?

    Kind regards,

    Filip

  • Filip,
    CCS6 allows you to use the free GNU-ARM compiler, plese see:
    processors.wiki.ti.com/.../Using_GCC_with_Tiva_in_CCSv6

    This would allow you to use the existing QP ports to GNU-ARM. I would highly recommend to go this path, instead of using the TI compiler.

    --MMS
  • Thank you Miro - this is definitely a good option if you have the freedom to set up your own toolchain.

    For anybody forced to use the TI compiler or still interested in this topic- the TI C/C++ compiler (as of version 5.2.2) still does not seem to provide an intrinsic function for masking interrupts. The TI DriverLib provides the CPU_basepriSet() API function in cpu.c but this may be too much overhead as Miro pointed out.

    I could not compile a working Blinky example on MSP432 using QPCPP 5.4.2 but the newest Git version already contains ports/arm-cm/q[v|k]/ti directories with SET_BASEPRI macro exactly as proposed by Miro but with hardcoded values for the BASEPRI register, i.e.

    #define QF_INT_DISABLE() __asm( \
            " PUSH {R0}\n\r" \
            " MOV  R0,#0x3F\n\r" \
            " MSR  BASEPRI,R0\n\r" \
            " POP  {R0}")
    
    #define QF_INT_ENABLE() __asm( \
            " PUSH {R0}\n\r" \
            " MOV  R0,#0\n\r" \
            " MSR  BASEPRI,R0\n\r" \
            " POP  {R0}")

    This works without problems with the TI compiler.