×
单片机 > 单片机程序设计 > 详情

ARM SVC模式源码

发布时间:2020-08-27 发布时间:
|

采用svc模式可以将SDK(API)和Application分离, 也可以省略很多重复的代码,比如在bootload中用到了usb, 然后在application中想复用的话,可以用svc

原理: 类似软件中断,中添加了一个软件中断源,通过中断源表明调用的函数接口


源程序如下:

#ifndef NRF_SVC__

#define NRF_SVC__


#ifdef SVCALL_AS_NORMAL_FUNCTION

#define SVCALL(number, return_type, signature) return_type signature

#else


#ifndef SVCALL

#if defined (__CC_ARM)

#define SVCALL(number, return_type, signature) return_type __svc(number) signature

#elif defined (__GNUC__)

#define SVCALL(number, return_type, signature)

  _Pragma("GCC diagnostic ignored "-Wunused-function"")

  _Pragma("GCC diagnostic push")

  _Pragma("GCC diagnostic ignored "-Wreturn-type"")

  __attribute__((naked)) static return_type signature

  {

    __asm(

        "svc %0n"

        "bx r14" : : "I" (number) : "r0"

    );

  }   

  _Pragma("GCC diagnostic pop")

#elif defined (__ICCARM__)

#define PRAGMA(x) _Pragma(#x)

#define SVCALL(number, return_type, signature)

PRAGMA(swi_number = number)

__swi return_type signature;

#else

#define SVCALL(number, return_type, signature) return_type signature  

#endif

#endif  // SVCALL


#endif  // SVCALL_AS_NORMAL_FUNCTION

#endif  // NRF_SVC__


void C_SVC_Handler(uint8_t svc_num, uint32_t * p_svc_args)

{

    switch (svc_num)

    {

        case NRF_SEC_SVC_HASH:

            p_svc_args[0] = nrf_sec_hash((nrf_sec_data_t *)     p_svc_args[0],

                                         (uint8_t *)            p_svc_args[1],

                                         (nrf_sec_hash_func_t)  p_svc_args[2]);

            break;


        case NRF_SEC_SVC_VERIFY:

            p_svc_args[0] = nrf_sec_verify((nrf_sec_data_t *)           p_svc_args[0],

                                           (nrf_sec_ecc_point_t *)      p_svc_args[1],

                                           (nrf_sec_ecc_signature_t *)  p_svc_args[2],

                                           (nrf_sec_algo_t)             p_svc_args[3]);

            break;

        

        default:

            p_svc_args[0] = NRF_ERROR_SVC_HANDLER_MISSING;

            break;

    }

}


#if defined ( __CC_ARM )

__asm void SVC_Handler(void)

{

EXC_RETURN_CMD_PSP  EQU 0xFFFFFFFD  ; EXC_RETURN using PSP for ARM Cortex. If Link register contains this value it indicates the PSP was used before the SVC, otherwise the MSP was used.


    IMPORT C_SVC_Handler

    LDR   R0, =EXC_RETURN_CMD_PSP   ; Load the EXC_RETURN into R0 to be able to compare against LR to determine stack pointer used. 

    CMP   R0, LR                    ; Compare the link register with R0. If equal then PSP was used, otherwise MSP was used before SVC.

    BNE   UseMSP                    ; Branch to code fetching SVC arguments using MSP.

    MRS   R1, PSP                   ; Move PSP into R1.

    B     Call_C_SVC_Handler        ; Branch to Call_C_SVC_Handler below.

UseMSP

    MRS   R1, MSP                   ; MSP was used, therefore Move MSP into R1.

Call_C_SVC_Handler

    LDR   R0, [R1, #24]             ; The arguments for the SVC was stacked. R1 contains Stack Pointer, the values stacked before SVC are R0, R1, R2, R3, R12, LR, PC (Return address), xPSR. 

                                    ; R1 contains current SP so the PC of the stacked frame is at SP + 6 words (24 bytes). We load the PC into R0.

    SUBS  R0, #2                    ; The PC before the SVC is in R0. We subtract 2 to get the address prior to the instruction executed where the SVC number is located.

    LDRB  R0, [R0]                  ; SVC instruction low octet: Load the byte at the address before the PC to fetch the SVC number.

    LDR   R2, =C_SVC_Handler        ; Load address of C implementation of SVC handler.

    BX    R2                        ; Branch to C implementation of SVC handler. R0 is now the SVC number, R1 is the StackPointer where the arguments (R0-R3) of the original SVC are located.

    ALIGN

}

#elif defined ( __GNUC__ )

void __attribute__ (( naked )) SVC_Handler(void)

{

    const uint32_t exc_return = 0xFFFFFFFD;      // EXC_RETURN using PSP for ARM Cortex. If Link register contains this value it indicates the PSP was used before the SVC, otherwise the MSP was used.

    

    __asm volatile(

        "cmp   lr, %0tn"                       // Compare the link register with argument 0 (%0), which is exc_return. If equal then PSP was used, otherwise MSP was used before SVC.

        "bne   UseMSPtn"                       // Branch to code fetching SVC arguments using MSP.

        "mrs   r1, psptn"                      // Move PSP into R1.

        "b     Call_C_SVC_Handlertn"           // Branch to Call_C_SVC_Handler below.

        "UseMSP:  tn"                          //

        "mrs   r1, msptn"                      // MSP was used, therefore Move MSP into R1.

        "Call_C_SVC_Handler:  tn"              //

        "ldr   r0, [r1, #24]tn"                // The arguments for the SVC was stacked. R1 contains Stack Pointer, the values stacked before SVC are R0, R1, R2, R3, R12, LR, PC (Return address), xPSR. 

                                                 // R1 contains current SP so the PC of the stacked frame is at SP + 6 words (24 bytes). We load the PC into R0.

        "sub   r0, r0, #2tn"                   // The PC before the SVC is in R0. We subtract 2 to get the address prior to the instruction executed where the SVC number is located.

        "ldrb  r0, [r0]tn"                     // SVC instruction low octet: Load the byte at the address before the PC to fetch the SVC number.

        "bx    %1tn"                           // Branch to C implementation of SVC handler, argument 1 (%1). R0 is now the SVC number, R1 is the StackPointer where the arguments (R0-R3) of the original SVC are located.

        ".aligntn"

        :: "r" (exc_return), "r" (C_SVC_Handler) // Argument list for the gcc assembly. exc_return is %0, C_SVC_Handler is %1.

        : "r0", "r1"                             // List of register maintained manually.

    );

}

#else

#error Compiler not supported.

#endif



__asm void SVCHandler(void)

{

    IMPORT SVCHandler_main

    TST lr, #4

    ITE EQ

    MRSEQ R0, MSP

    MRSNE R0, PSP

    B SVCHandler_main

}

void SVCHandler_main(unsigned int * svc_args)

{

    unsigned int svc_number;

    /*

    * Stack contains:

    * R0, R1, R2, R3, R12, R14, the return address and xPSR

    * First argument (R0) is svc_args[0]

    */

    svc_number = ((char *)svc_args[6])[-2];

    switch(svc_number)

    {

        case SVC_00:

/*


『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
浅谈AVR中定时器几种工作模式